<?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: Leonardo Colman Lopes</title>
    <description>The latest articles on DEV Community by Leonardo Colman Lopes (@leocolman).</description>
    <link>https://dev.to/leocolman</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%2F169802%2F230a62be-5c4c-44df-9182-479102ef8eb3.jpg</url>
      <title>DEV Community: Leonardo Colman Lopes</title>
      <link>https://dev.to/leocolman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leocolman"/>
    <language>en</language>
    <item>
      <title>Expand your digital picture frame use cases with Pi3D integrated USB media stick support using UDiskie</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Mon, 10 Mar 2025 21:03:28 +0000</pubDate>
      <link>https://dev.to/leocolman/expand-your-digital-picture-frame-use-cases-with-pi3d-integrated-usb-media-stick-support-using-2737</link>
      <guid>https://dev.to/leocolman/expand-your-digital-picture-frame-use-cases-with-pi3d-integrated-usb-media-stick-support-using-2737</guid>
      <description>&lt;p&gt;I built and customized three instances of &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/" rel="noopener noreferrer"&gt;The Digital Picture Frame&lt;/a&gt;. It’s so simple yet such a great system setup that I fell in love.&lt;/p&gt;

&lt;p&gt;Today I want to share a method to use USB Media Sticks to view our photos. This method uses &lt;code&gt;Udiskie&lt;/code&gt;, while the traditional method (explained by Wolfgang Männel in &lt;a href="https://www.thedigitalpictureframe.com/expand-your-digital-picture-frame-use-cases-with-pi3d-integrated-usb-media-stick-support/" rel="noopener noreferrer"&gt;Expand your digital picture frame use cases with Pi3D integrated USB media stick support&lt;/a&gt;) uses python scripts to achieve a very similar goal.&lt;/p&gt;

&lt;p&gt;I recommend reading both articles and picking the method you feel more comfortable in. Both methods are mostly feature compatible in the end -- having an auto-mounted USB device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Setup Does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://github.com/coldfix/udiskie?tab=readme-ov-file" rel="noopener noreferrer"&gt;Udiskie&lt;/a&gt; to manage USB auto-loading/reloading&lt;/li&gt;
&lt;li&gt;Modify Picture Frame's configuration to read photos from the automount directory&lt;/li&gt;
&lt;li&gt;Prepare the system to have &lt;code&gt;Udiskie&lt;/code&gt; running when the system boots&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing Udiskie
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;udiskie is a udisks2 front-end that allows to manage removable media such as CDs or flash drives from userspace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's install &lt;code&gt;udiskie&lt;/code&gt; by running one simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install udiskie udisks2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And although we won't use it, udiskie has a cool UI!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2poq6jkzcseok22p54i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2poq6jkzcseok22p54i.png" alt="udiskie interface" width="750" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Udiskie Auto-Start
&lt;/h2&gt;

&lt;p&gt;To setup udiskie to work without password prompt and on boot we must first install PolicyKit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install policykit-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then let's create a policy configuration with required permissions. Let's create the file with &lt;code&gt;sudo nano /etc/polkit-1/rules.d/99-udisks2.rules&lt;/code&gt; and the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;polkit.addRule(function(action, subject) {
    if (action.id.indexOf("org.freedesktop.udisks2.filesystem-mount") === 0 &amp;amp;&amp;amp;
        subject.isInGroup("plugdev")) {
        return polkit.Result.YES;
    }
});

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

&lt;/div&gt;



&lt;p&gt;Finally, let's create a &lt;code&gt;udiskie.service&lt;/code&gt; file to ensure udiskie is working all the time. Let's use &lt;code&gt;sudo nano /etc/systemd/system/udiskie.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Auto-mount USB drives with udiskie
After=graphical.target network-online.target

[Service]
User=pi
ExecStart=/usr/bin/udiskie --automount
Restart=on-failure

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let's restart all services that are used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable polkit
sudo systemctl enable udisks2
sudo systemctl enable udiskie

sudo systemctl restart polkit
sudo systemctl restart udisks2
sudo systemctl restart udiskie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we're done with &lt;code&gt;udiskie&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying PicFrame Configuration
&lt;/h2&gt;

&lt;p&gt;Configuration change to PicFrame is simple. We're going to change the photo directory to &lt;code&gt;/media&lt;/code&gt;, the location where drives are mounted (e.g. &lt;code&gt;/media/pi/E342-JS2J&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano picframe_data/config/configuration.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And inside model, let's change this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pic_dir: "/media" # default="/home/pi/Pictures", root folder for images

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

&lt;/div&gt;



&lt;p&gt;And we are done with PicFrame Configuration! With this we should be done! Reboot the system and insert a flash drive or two to see if pictures from all drives are coming to PicFrame.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Converting an old TV Box into an epic Picture Frame</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Fri, 07 Mar 2025 16:18:15 +0000</pubDate>
      <link>https://dev.to/leocolman/converting-an-old-tv-box-into-an-epic-picture-frame-3iob</link>
      <guid>https://dev.to/leocolman/converting-an-old-tv-box-into-an-epic-picture-frame-3iob</guid>
      <description>&lt;p&gt;I built and customized 3 instances of &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/" rel="noopener noreferrer"&gt;The Digital Picture Frame&lt;/a&gt;. It's so simple yet such a great system setup that I fell in love.&lt;/p&gt;

&lt;p&gt;People have built such &lt;a href="https://www.thedigitalpictureframe.com/the-digital-picture-frame-user-gallery-with-great-examples-and-inspiration-for-your-raspberry-pi-projects/" rel="noopener noreferrer"&gt;awesome setups&lt;/a&gt; for this small utility, and passion drove me to my calling: It's my time to hack a device and turn it into a Picture Frame.&lt;/p&gt;

&lt;p&gt;My chosen target? An old TV Box:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99ebhx0kozy9k2zc2lz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99ebhx0kozy9k2zc2lz8.png" alt="TV Box" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It has everything needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Hackable Android TV that I can turn into Linux&lt;/li&gt;
&lt;li&gt;A way to boot a custom system (i.e. an SD card)&lt;/li&gt;
&lt;li&gt;A way to mount different media for photo display (i.e. Flash Drives)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in this journey and perhaps you will have a TV Box-Frame too!&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;A Word of Warning First&lt;/strong&gt;:&lt;br&gt;
Not all TV Boxes will work. Some (if not most) are not able to host a custom system. Be wary when following this as a guide as your setup might vary.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Armbian
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/armbian" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt; is a "Minimal custom Debian or Ubuntu based Linux perfect for server and IOT". We're going to pick it as most TV Boxes are ARM-based, and Armbian is perfect for the job.&lt;/p&gt;

&lt;p&gt;We are going to follow the guide &lt;a href="https://forum.armbian.com/topic/34923-csc-armbian-for-rk322x-tv-box-boards/" rel="noopener noreferrer"&gt;CSC Armbian for RK322x TV box boards&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxk1wpvkmlfpq2voervxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxk1wpvkmlfpq2voervxf.png" alt="Armbian" width="481" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For this tutorial we're going to use a &lt;code&gt;RK3229&lt;/code&gt; as our pilot chip&lt;/strong&gt;. It's available in many TV Boxes and is not hard to setup&lt;/p&gt;
&lt;h3&gt;
  
  
  Discovering the Chipset
&lt;/h3&gt;

&lt;p&gt;If you're not sure that your TV Box chip is supported, download and install through the TV Box the app &lt;code&gt;Z-CPU&lt;/code&gt; or the FLOSS App &lt;a href="https://f-droid.org/en/packages/com.kgurgul.cpuinfo/" rel="noopener noreferrer"&gt;&lt;code&gt;CPU Info&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the app we're looking for information on the chipset. &lt;strong&gt;Chipsets of the format &lt;code&gt;RK322X&lt;/code&gt; are supported.&lt;/strong&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Unbricking Instructions
&lt;/h3&gt;

&lt;p&gt;Technically, rockchip devices &lt;strong&gt;cannot be bricked&lt;/strong&gt;. In some cases a screw-up might require repairing of the original system, and the unbricking instructions from &lt;a href="https://forum.armbian.com/topic/34923-csc-armbian-for-rk322x-tv-box-boards/" rel="noopener noreferrer"&gt;CSC Armbian for RK322x TV box boards&lt;/a&gt; can help.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing Using SD Card
&lt;/h3&gt;

&lt;p&gt;We're going to use an SDXC Card (Or most commonly an SD Card + Adapter). This card will hold our entire operating system*, and will be plugged in the machine 24/7. I recommend anything 8GB or bigger, but realistically 3GB is the entire system + files.&lt;/p&gt;

&lt;p&gt;* You could instead install the system directly to the machine. I couldn't get that to work reliably and preferred the permanent SD approach.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preparing the TV Box with Multitool
&lt;/h3&gt;

&lt;p&gt;The Multitool is a small but powerful tool to make changes to our RK322X.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://users.armbian.com/jock/web/rk322x/multitool/multitool.img.xz" rel="noopener noreferrer"&gt;Multitool - A small but powerful image for RK322x TV Box maintenance
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's flash the Multitool image onto the SD Card first. You can use any tool for it, but I like &lt;a href="https://etcher.balena.io/" rel="noopener noreferrer"&gt;Balena Etcher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwy14ptx5a9e41eeot7bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwy14ptx5a9e41eeot7bv.png" alt="Balena Etcher" width="798" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our goal with Multitool is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flash Multitool to the SD Card&lt;/li&gt;
&lt;li&gt;Put the card in the TV Box and power on. After a few seconds Multitool should appear on the display&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;RECOMMENDED&lt;/em&gt;&lt;/strong&gt; Make a backup of the current firmware with &lt;strong&gt;Backup Flash&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Install Jump Start for Armbian&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faipj6gdi0evz9pf8uur2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faipj6gdi0evz9pf8uur2.png" alt="Multitool" width="792" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we're done with Multitool! Power the device down and retrieve the SD card. Store your backup and get ready to flash it again!&lt;/p&gt;
&lt;h3&gt;
  
  
  Choosing an Image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://forum.armbian.com/topic/34923-csc-armbian-for-rk322x-tv-box-boards/" rel="noopener noreferrer"&gt;CSC Armbian for RK322x TV box boards&lt;/a&gt; explains how to make a build, but we are going to grab a pre-built binary from &lt;a href="https://github.com/armbian/community/releases/latest" rel="noopener noreferrer"&gt;armbian/community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymrpfewywydz3emretfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymrpfewywydz3emretfx.png" alt="Armbian" width="800" height="123"&gt;&lt;/a&gt;&lt;br&gt;
Let's take the image &lt;code&gt;Rk322x-box_bookworm_current_6.12.15_minimal.img.xz&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As we've done earlier, let's flash it to our SD card using Balena Etcher.&lt;br&gt;
After flashing, insert the SD with the new Armbian image on the device and power it on.&lt;/p&gt;
&lt;h3&gt;
  
  
  Welcome to Armbian
&lt;/h3&gt;

&lt;p&gt;If everything worked correctly, you should now have a shell inside Armbian asking you for information.&lt;br&gt;
The most important point here is that we'll create a normal user account with the username &lt;code&gt;pi&lt;/code&gt;. This will be important to follow &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/" rel="noopener noreferrer"&gt;The Digital Picture Frame&lt;/a&gt; without too many changes on the scripts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvdfpgvp8b3wy591dv9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvdfpgvp8b3wy591dv9t.png" alt="Armbian" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To finish configuring Armbian, let's run two commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo rk322x-config&lt;/code&gt; and select your board characteristics to enable leds, wifi chips, etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;armbian-config&lt;/code&gt; to configure timezone, locales and other personal options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point you can also connect your device to your network and connect to it through &lt;code&gt;ssh&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install The Digital PictureFrame
&lt;/h3&gt;

&lt;p&gt;From this point on we are done with the system and can start playing with PictureFrame itself. We are going to follow &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/" rel="noopener noreferrer"&gt;the guide&lt;/a&gt; for Raspberries as close as we can until we get it working.&lt;/p&gt;

&lt;p&gt;Let's first install necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install build-essential pkg-config python3-dev python3-pip python3.11-venv libjpeg-dev libtiff-dev libfreetype6-dev zlib1g-dev liblcms2-dev libwebp-dev tcl-dev tk-dev libharfbuzz-dev libfribidi-dev libxcb1-dev libpng-dev cmake ninja-build git libgles2-mesa libegl1-mesa libgl1-mesa-dri libglapi-mesa libsdl2-2.0-0 libsdl2-dev

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

&lt;/div&gt;



&lt;p&gt;And compile + install HEIF, as the ones from Armbian aren't updated enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
git clone https://github.com/strukturag/libheif.git
cd libheif

mkdir build &amp;amp;&amp;amp; cd build
cmake ..
make -j$(nproc)
sudo make install

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

&lt;/div&gt;



&lt;p&gt;Then let's prepare a Python VEnv to install picframe into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
mkdir venv_picframe

python3 -m venv /home/pi/venv_picframe

source venv_picframe/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, let's install PicFrame!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install picframe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To know it worked, we should see after a long (maybe 30+ minutes) some positive messages:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Successfully installed IPTCInfo3-2.1.4 Pillow-11.1.0 PyYAML-6.0.2 defusedxml-0.7.1 ninepatch-0.2.0 numpy-2.2.3 paho-mqtt-2.1.0 pi-heif-0.21.0 pi3d-2.53 picframe-2024.11.1 pysdl2-0.9.17&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configuring PicFrame
&lt;/h3&gt;

&lt;p&gt;On &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/#Configuring_PictureFrame" rel="noopener noreferrer"&gt;Configuring PicFrame&lt;/a&gt; we have clear instructions on how to configure picframe. Let's do that now!&lt;/p&gt;

&lt;p&gt;You will be asked three questions for the basic configuration settings. Just hit Enter to keep the default for now. You can always change these settings later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir {Pictures,DeletedPictures}

picframe -i /home/pi/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t need to make changes in the configuration file at this point. But you probably want to customize it later. In that case, open it with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano ~/picframe_data/config/configuration.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting Picframe
&lt;/h3&gt;

&lt;p&gt;Let's create a script to start our Picframe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano start_picframe.sh

#!/bin/bash
source /home/pi/venv_picframe/bin/activate # activate phyton virtual env
picframe &amp;amp;  #start picframe

while true; do sleep 10; done # Keep the script running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close and make the file executable with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x ./start_picframe.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, finally, let's run our PicFrame&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./start_picframe.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if everything went through correctly, we'll get a beautiful mug on the screen:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Auto Starting Picframe
&lt;/h3&gt;

&lt;p&gt;This part will work a bit different from &lt;a href="https://www.thedigitalpictureframe.com/how-to-build-the-best-raspberry-pi-digital-picture-frame-with-bookworm-wayland-2025-edition-pi-2-3-4-5/#Autostarting_PictureFrame" rel="noopener noreferrer"&gt;Autostarting PictureFrame&lt;/a&gt;, so pay close attention!&lt;/p&gt;

&lt;p&gt;Let's start by creating the file &lt;code&gt;picframe.service&lt;/code&gt; with &lt;code&gt;sudo nano /etc/systemd/system/picframe.service&lt;/code&gt; and pasting the following content inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=PictureFrame Startup
After=multi-user.target

[Service]
Type=simple
ExecStart=/bin/bash /home/pi/start_picframe.sh
Restart=on-failure  # Only restart if it fails
RestartSec=10       # Wait 10 seconds before restarting
User=pi
WorkingDirectory=/home/pi
Environment=DISPLAY=:0
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reload systemd and enable the brand new service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl enable picframe.service
sudo systemctl start picframe.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can check the service status by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl status picframe.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Autologin on tty1
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;pi&lt;/code&gt; user doesn't have auto-login enabled, and we must have it if we don't want to stare at a login screen. Let's change that by creating an override for &lt;code&gt;getty@tty1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /etc/systemd/system/getty@tty1.service.d
sudo nano /etc/systemd/system/getty@tty1.service.d/override.conf

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

&lt;/div&gt;



&lt;p&gt;And paste this inside to enable auto-login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin pi --noclear %I $TERM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PictureFrame should now start on boot!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Automated Screenshot Taking in Android using Kotest and Ktor</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Sun, 26 Jan 2025 12:43:19 +0000</pubDate>
      <link>https://dev.to/leocolman/automated-screenshot-taking-in-android-using-kotest-and-ktor-o71</link>
      <guid>https://dev.to/leocolman/automated-screenshot-taking-in-android-using-kotest-and-ktor-o71</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Automated screenshot testing is essential in Android development for ensuring UI consistency across updates and devices. Manually verifying UI changes is time-consuming and error-prone, making automation a vital practice.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll show how to use Kotest for capturing screenshots and Ktor for uploading them to a local server. This setup streamlines UI validation, improves efficiency, and centralizes screenshot storage for better collaboration and quality assurance.&lt;/p&gt;

&lt;p&gt;This article contains a simpler implementation of what's currently in production on &lt;a href="https://github.com/LeoColman/Petals" rel="noopener noreferrer"&gt;Petals App&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Setup
&lt;/h2&gt;

&lt;p&gt;To get started with automated screenshot testing using Kotest and Ktor, we’ll need to prepare our project by configuring the necessary tools and libraries.&lt;/p&gt;

&lt;p&gt;Update our project’s &lt;em&gt;build.gradle.kts&lt;/em&gt; file to include the required libraries for testing and screenshot uploads. Below are the key dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    // Kotest for writing tests
    androidTestImplementation("br.com.colman:kotest-runner-android:${version}")

    // Fuel library for uploading screenshots
    androidTestImplementation("com.github.kittinunf.fuel:fuel:${version}")
}

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

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kotest.io" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt; provides the framework for writing and running tests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/LeoColman/kotest-android/tree/main" rel="noopener noreferrer"&gt;Kotest-Android&lt;/a&gt; provides specific bindings for running Android tests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kittinunf/fuel" rel="noopener noreferrer"&gt;Fuel&lt;/a&gt; simplifies HTTP requests for uploading screenshots during tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Preparing a Local Server to Receive Screenshots
&lt;/h2&gt;

&lt;p&gt;A critical component of automated screenshot testing is a server to store the captured images for validation or archival purposes. We’ll use Ktor, a lightweight Kotlin-based framework, to set up a local server capable of handling screenshot uploads.&lt;/p&gt;

&lt;p&gt;Create a Kotlin script file named &lt;em&gt;refresh_screenshots.main.kts&lt;/em&gt;. This script will configure and launch a local server using Ktor.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1. Disabling Animations
&lt;/h3&gt;

&lt;p&gt;Animations in the Android emulator can affect screenshot consistency. The script disables them using ADB commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun disableAnimations() {
    listOf(
        "adb shell settings put global window_animation_scale 0",
        "adb shell settings put global transition_animation_scale 0",
        "adb shell settings put global animator_duration_scale 0"
    ).forEach { command -&amp;gt;
        try {
            val process = Runtime.getRuntime().exec(command)
            process.waitFor()
        } catch (e: Exception) {
            println("Error while trying to disable animations via ADB: ${e.localizedMessage}")
        }
    }
    println("Animations in the emulator have been disabled")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2. Starting the Ktor Server
&lt;/h3&gt;

&lt;p&gt;The server listens on port 8081 and handles file uploads. Uploaded screenshots are stored in language-specific directories based on parameters (lang and country) in the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun startServer(): NettyApplicationEngine {
    return embeddedServer(Netty, port = 8081) {
        install(ContentNegotiation) {
            json()
        }
        routing {
            post("/upload") {
                val multipart = call.receiveMultipart()
                val lang = call.parameters["lang"] ?: "unknown"
                val country = call.parameters["country"] ?: "unknown"

                multipart.forEachPart { part -&amp;gt;
                    if (part is PartData.FileItem) {
                        val countryHyphen = if (country.isNotBlank()) "-$country" else ""
                        val dir = File("../fastlane/metadata/android/${lang}${countryHyphen}/images/phoneScreenshots")
                        if (!dir.exists()) {
                            dir.mkdirs()
                        }
                        val file = File(dir, part.originalFileName ?: "screenshot.png")
                        part.streamProvider().use { input -&amp;gt; file.outputStream().buffered().use { input.copyTo(it) } }
                    }
                    part.dispose()
                }
                call.respondText("File uploaded successfully", status = HttpStatusCode.OK)
            }
        }
    }.start(wait = false)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3. Running Tests and Handling the Server
&lt;/h3&gt;

&lt;p&gt;The script runs Android tests using Gradle and stops the server once the tests are completed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val server = startServer()

disableAnimations()

// Run Android tests
val process = ProcessBuilder(
    "../gradlew",
    "connectedFdroidDebugAndroidTest",
    "-Pandroid.testInstrumentationRunnerArguments.class=br.com.colman.petals.ScreenshotTakerTest"
).inheritIO().start()
val exitCode = process.waitFor()

server.stop(1000, 10000)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4. Full Script
&lt;/h3&gt;

&lt;p&gt;Let's take a look at the final result by merging all the scripts and adding relevant imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env kotlin

@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1")
@file:DependsOn("io.ktor:ktor-server-core-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-server-netty-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-server-content-negotiation-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-serialization-kotlinx-json-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-server-host-common-jvm:2.3.13")

import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.io.File

// Function to disable animations via ADB
fun disableAnimations() {
  listOf(
    "adb shell settings put global window_animation_scale 0",
    "adb shell settings put global transition_animation_scale 0",
    "adb shell settings put global animator_duration_scale 0"
  ).forEach { command -&amp;gt;
    try {
      val process = Runtime.getRuntime().exec(command)
      process.waitFor()
    } catch (e: Exception) {
      println("Error while trying to disable animations via ADB: ${e.localizedMessage}")
    }
  }
  println("Animations in the emulator have been disabled")
}

fun startServer(): NettyApplicationEngine {
  return embeddedServer(Netty, port = 8081) {
    install(ContentNegotiation) {
      json()
    }
    routing {
      post("/upload") {
        val multipart = call.receiveMultipart()
        val lang = call.parameters["lang"] ?: "unknown"
        val country = call.parameters["country"] ?: "unknown"

        multipart.forEachPart { part -&amp;gt;
          if (part is PartData.FileItem) {
            val countryHyphen = if (country.isNotBlank()) "-$country" else ""
            val dir = File("../fastlane/metadata/android/${lang}${countryHyphen}/images/phoneScreenshots")
            if (!dir.exists()) {
              dir.mkdirs()
            }
            val file = File(dir, part.originalFileName ?: "screenshot.png")
            part.streamProvider().use { input -&amp;gt; file.outputStream().buffered().use { input.copyTo(it) } }
          }
          part.dispose()
        }
        call.respondText("File uploaded successfully", status = HttpStatusCode.OK)
      }
    }
  }.start(wait = false)
}


// Start the server
val server = startServer()

// Disable animations
disableAnimations()

// Run Android tests
val process = ProcessBuilder(
  "../gradlew",
  "connectedFdroidDebugAndroidTest",
  "-Pandroid.testInstrumentationRunnerArguments.class=br.com.colman.petals.ScreenshotTakerTest"
).inheritIO().start()
val exitCode = process.waitFor()

// Stop the server
server.stop(1000, 10000)

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Writing a Screenshot Test in Kotest
&lt;/h2&gt;

&lt;p&gt;This section demonstrates how to create comprehensive screenshot tests using Kotest in an Android project. The provided code integrates Jetpack Compose testing with screenshot capture and upload functionality, supporting multiple locales and organized storage on a local server.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1. Key Features of the Screenshot Tests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Multi-Locale Testing&lt;/strong&gt;&lt;br&gt;
The tests iterate over a list of language (lang) and country (country) codes, ensuring the UI is tested across different locales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Screenshot Capture&lt;/strong&gt;&lt;br&gt;
Screenshots are taken directly from the UI during tests using Jetpack Compose's testing utilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Automated Uploads&lt;/strong&gt;&lt;br&gt;
Screenshots are uploaded to a local server via HTTP, organized into directories by language and country.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.2. Multi-Locale Screenshot Tests
&lt;/h3&gt;

&lt;p&gt;Below is the main test class, which runs multiple tests and captures screenshots at different points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ScreenshotTakerTest : FunSpec({

    val locales = listOf(
        "de" to "DE",
        "en" to "US",
        "es" to "ES",
        "fr" to "FR",
        "it" to "IT",
        "pt" to "BR",
        "ru" to "RU",
        "tr" to "TR",
        "uk" to ""
    )

    test("Screenshot for Main Page") {
        runAndroidComposeUiTest&amp;lt;MainActivity&amp;gt; {
            locales.forEach { (lang, country) -&amp;gt;
                activity?.setLocale(Locale(lang, country))
                waitForIdle()

                // Interact with UI
                onNodeWithTag("UsageMainColumn").performTouchInput { swipeUp() }
                waitForIdle()

                // Capture and upload screenshot
                takeScreenshot("main_page.png", lang, country)
            }
        }
    }
})

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3. Helper Functions
&lt;/h3&gt;

&lt;p&gt;The implementation relies on utility functions for common operations.&lt;/p&gt;

&lt;p&gt;Setting the App Locale Updates the app’s locale dynamically during tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun MainActivity.setLocale(locale: Locale) {
    val resources = baseContext.resources
    Locale.setDefault(locale)
    val config = resources.configuration
    config.setLocale(locale)
    resources.updateConfiguration(config, resources.displayMetrics)
    runOnUiThread { recreate() }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capturing a Screenshot Uses Jetpack Compose's &lt;em&gt;captureToImage()&lt;/em&gt; to capture the current UI state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun AndroidComposeUiTest&amp;lt;*&amp;gt;.takeScreenshot(file: String, lang: String, country: String) {
    sleep(3000) // Wait for animations to settle
    val bitmap = onRoot().captureToImage().asAndroidBitmap()
    uploadScreenshot(bitmap, file, lang, country)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uploading the Screenshot Sends the captured screenshot to the local server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun uploadScreenshot(bitmap: Bitmap, fileName: String, lang: String, country: String) {
    val tempFile = File.createTempFile(fileName, null).apply {
        outputStream().use { bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) }
    }
    Fuel.upload("http://10.0.2.2:8081/upload?lang=$lang&amp;amp;country=$country")
        .add(FileDataPart(tempFile, name = "file", filename = fileName))
        .response()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;em&gt;10.0.2.2&lt;/em&gt; is the ip address of the computer running the emulator. It's an &lt;a href="https://developer.android.com/studio/run/emulator-networking" rel="noopener noreferrer"&gt;special address&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;Automating screenshot testing in Android using Kotest and Ktor streamlines the UI validation process and ensures consistency across multiple locales. By combining Jetpack Compose testing utilities with an efficient local server, developers can easily capture, organize, and analyze screenshots during automated tests.&lt;/p&gt;

&lt;p&gt;This approach offers several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved Efficiency: Automates tedious manual testing tasks.&lt;/li&gt;
&lt;li&gt;Comprehensive Coverage: Validates UI behavior across different languages, countries, and configurations.&lt;/li&gt;
&lt;li&gt;Centralized Storage: Screenshots are uploaded to a server and organized systematically for future analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adopting this solution, teams can reduce testing overhead, improve collaboration, and ensure a polished, reliable user interface. This setup can further be extended with CI/CD pipelines, enabling seamless integration into modern development workflows.&lt;/p&gt;

&lt;p&gt;Start implementing automated screenshot testing today to enhance your app’s quality and streamline your development process.&lt;/p&gt;

</description>
      <category>kotest</category>
      <category>kotlin</category>
      <category>ktor</category>
      <category>android</category>
    </item>
    <item>
      <title>Validar CPF facilmente em Kotlin</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Wed, 26 Aug 2020 23:14:09 +0000</pubDate>
      <link>https://dev.to/leocolman/validando-cpf-de-maneira-facil-em-kotlin-1kbf</link>
      <guid>https://dev.to/leocolman/validando-cpf-de-maneira-facil-em-kotlin-1kbf</guid>
      <description>&lt;h3&gt;
  
  
  NL;NL
&lt;/h3&gt;

&lt;p&gt;Desenvolvi a biblioteca &lt;a href="https://github.com/LeoColman/SimpleCpfValidator" rel="noopener noreferrer"&gt;SimpleCPFValidator&lt;/a&gt; para facilitar validações de CPF em Kotlin.&lt;/p&gt;

&lt;h1&gt;
  
  
  Olá, Kotliners!
&lt;/h1&gt;

&lt;p&gt;Todo novo cadastro de usuário em todo aplicativo segue sempre o mesmo padrão. Pedimos nome, data de nascimento e &lt;strong&gt;CPF&lt;/strong&gt; de nossos queridos usuários quase que rotineiramente.&lt;/p&gt;

&lt;p&gt;Quem já brincou de validar CPF sabe que é uma chatice contar todos os números, fazer valer com o digito verificador e zás e zás...&lt;/p&gt;

&lt;h2&gt;
  
  
  A validação
&lt;/h2&gt;

&lt;p&gt;As regras de validação em si são bem simples, e estão facilmente disponibilizadas pela web, além das &lt;a href="http://normas.receita.fazenda.gov.br/sijut2consulta/link.action?visao=anotado&amp;amp;idAto=1893" rel="noopener noreferrer"&gt;normas oficiais da receita&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CPFs com números repetidos (111.111.111-11, 222.222.222-22, ... 999.999.999-99) não são válidos&lt;/li&gt;
&lt;li&gt;O CPF 000.000.001-91 é válido apenas para representar pessoas sem CPF&lt;/li&gt;
&lt;li&gt;O cálculo maluco de digito verificador&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esta última regra é mais chata de entender. Este &lt;a href="https://www.somatematica.com.br/faq/cpf.php" rel="noopener noreferrer"&gt;artigo do Só Matemática&lt;/a&gt; explica bem como funciona, mas se tiver interesse, leia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O número do CPF é escrito na forma ABCDEFGHI/JK

O J e o K são o 1̣° e 2° dígitos verificadores

Para obter J multiplicamos A, B, C, D, E, F, G, H e I 
pelas constantes correspondentes:
| A   | B  | C  | D  | E  | F  | G  | H  | I  |
| 10x | 9x | 8x | 7x | 6x | 5x | 4x | 3x | 2x |
O resultado de 10A + 9B + ... + 2I é então dividido por 11, e
o valor que RESTAR da divisão será utilizado. Se o resto for
0 ou 1, J = 0. Caso contrário, J = 11 - resto

Para obter K. seguimos a mesma regra, porém com J no final 
da tabela, e começaremos a conta a partir de 11:
| A   | B   | C  | D  | E  | F  | G  | H  | I  | J  |
| 11x | 10x | 9x | 8x | 7x | 6x | 5x | 4x | 3x | 2x |
O resultado de 11A + ... + 2J é então dividido 
por 11, e a regra do resto será a mesma que o J.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  É fácil, não?
&lt;/h3&gt;

&lt;p&gt;As regras são simples, não são? Fáceis de entender do ponto de vista matemático e do ponto de vista de código. Então &lt;strong&gt;Por que é sempre tão chato implementar isso?&lt;/strong&gt; Ou copiamos códigos de outros projetos, ou escrevemos um espaguete para seguir essas regras. Testar é complicado, e no final fica uma gororoba.&lt;/p&gt;

&lt;p&gt;Essa gororoba precisa de manutenção, no entanto, e estará amarrada no nosso projeto pra sempre. Não queremos isso!&lt;/p&gt;

&lt;h1&gt;
  
  
  A maneira fácil
&lt;/h1&gt;

&lt;p&gt;Em Kotlin temos a tendência de &lt;strong&gt;evitar boilerplates&lt;/strong&gt;, e não carregar nosso código de tanta coisa.&lt;/p&gt;

&lt;p&gt;Com isso em mente, desenvolvi a biblioteca open source &lt;a href="https://github.com/LeoColman/SimpleCpfValidator" rel="noopener noreferrer"&gt;SimpleCPFValidator&lt;/a&gt;. Ela colocará em seu projeto alguns utilitários para validação de CPF.&lt;/p&gt;

&lt;p&gt;Busquei as regras originais para validação, e desenvolvi funções de extensão em cima disso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userCpf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userCpf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userCpf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isCpf&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Is CPF!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E simples assim, seu código conhece as regras de validação de CPF!&lt;/p&gt;

&lt;p&gt;Por padrão, os caracteres &lt;code&gt;.&lt;/code&gt; e &lt;code&gt;-&lt;/code&gt; são retirados da String (permitindo o formato &lt;code&gt;123.456.789-11&lt;/code&gt;, por exemplo), mas isso pode ser modificado através do parâmetro &lt;code&gt;charactersToIgnore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="s"&gt;"123.456.789/11"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isCpf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;charactersToIgnore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para utilizar essa lib em seu projeto,coloque em seu build.gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation("br.com.colman.simplecpfvalidator:simple-cpf-validator:{version}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kotlin</category>
    </item>
    <item>
      <title>Writing reusable tests with Kotest's Test Factory</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Thu, 16 Apr 2020 20:02:59 +0000</pubDate>
      <link>https://dev.to/kotest/writing-reusable-tests-with-kotest-s-test-factory-5gi</link>
      <guid>https://dev.to/kotest/writing-reusable-tests-with-kotest-s-test-factory-5gi</guid>
      <description>&lt;p&gt;TL;DR - How to use &lt;a href="https://github.com/kotest/kotest" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt; 4.0.0 new Test Factory feature to include reusable blocks of test in your test suite&lt;/p&gt;

&lt;h1&gt;
  
  
  Hello, Kotliners!
&lt;/h1&gt;

&lt;p&gt;In this article we'll see how to use a feature from the &lt;a href="https://github.com/kotest/kotest" rel="noopener noreferrer"&gt;Kotest Framework&lt;/a&gt; to create reusable tests in your test suite, for cases in which you test multiple classes exactly the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring a Test Factory function
&lt;/h2&gt;

&lt;p&gt;It's very easy to create a reusable test. You must first pick your favourite style among &lt;a href="https://github.com/kotest/kotest/blob/master/doc/styles.md" rel="noopener noreferrer"&gt;Kotest's test styles&lt;/a&gt;. You can pick any of them, but my personal favourite (and the one I'll use here) is &lt;code&gt;FunSpec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Declaring a test using &lt;code&gt;FunSpec&lt;/code&gt; is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySpec&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Foo should not be equal to bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"foo"&lt;/span&gt; &lt;span class="n"&gt;shouldNotBe&lt;/span&gt; &lt;span class="s"&gt;"bar"&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;Declaring the same test, but this time as a test factory is very similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top level val&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;myReusableTest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;funSpec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Foo should not be equal to bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"foo"&lt;/span&gt; &lt;span class="n"&gt;shouldNotBe&lt;/span&gt; &lt;span class="s"&gt;"bar"&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;However, there are some cases in which having the same test will produce a different result if called multiple times. For cases which our tests depend on some parameters, we might rather want to use use this as a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;myReusableTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myParameter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;funSpec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$myParameter should not be equal to bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;myParameter&lt;/span&gt; &lt;span class="n"&gt;shouldNotBe&lt;/span&gt; &lt;span class="s"&gt;"bar"&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;Both cases can be included in any &lt;code&gt;Spec&lt;/code&gt; by using the &lt;code&gt;include&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My usual test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;myReusableTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;myReusableTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Another usual test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&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;And as expected, the tests from the test factory are applied!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstp0ysvjghtk8ayy4vbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstp0ysvjghtk8ayy4vbw.png" width="398" height="197"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A small example
&lt;/h2&gt;

&lt;p&gt;Now that we understand the basic concept, let's play a little bit with a more realistic example: Calculators! &lt;del&gt;really realistic, everyone implements calculators&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;Let's suppose we have 3 types of calculators:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Sum Calculator&lt;/li&gt;
&lt;li&gt;A Subtraction Calculator&lt;/li&gt;
&lt;li&gt;A Complete Calculator, that can do both operations.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SumOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;SumCalculator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SumOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SubtractionOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;SubtractionCalculator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SubtractionOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To test these implementations, we would create a &lt;code&gt;Spec&lt;/code&gt; for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SumCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sum 2 and 2 should be 4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SumCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubtractionCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4 minus 2 should be 2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SubtractionCalculatorTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;2&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;When implementing our &lt;code&gt;CompleteCalculator&lt;/code&gt;, we should test these behaviours as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;CompleteCalculator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SumOp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SubtractionOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompleteCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sum 2 and 2 should be 4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CompleteCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4 minus 2 should be 2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CompleteCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;2&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;We have ended up writing the same test that we already wrote for sum calc and sub calc. This is duplication that we'd like to try and avoid.&lt;/p&gt;

&lt;p&gt;Now let's exercise what we learned previously. Writing tests through composition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sumTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SumOp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;funSpec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sum 2 and 2 should be 4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;subtractionTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SubtractionOp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;funSpec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4 minus 2 should be 2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SumCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sumTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SumCalculator&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubtractionCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;subtractionTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SubtractionCalculator&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompleteCalculatorTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sumTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CompleteCalculator&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;subtractionTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CompleteCalculator&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;And &lt;em&gt;voilà!&lt;/em&gt; We successfully reused a test for more than one scenario!&lt;/p&gt;

&lt;h2&gt;
  
  
  What could this be useful for?
&lt;/h2&gt;

&lt;p&gt;In the real world we won't be implementing calculators as often as we want to. So, &lt;strong&gt;why would I use this kind of factory and composition&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;We created these &lt;strong&gt;Test Factories&lt;/strong&gt; for any kind of repeatable test pattern. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need to test that every &lt;code&gt;Repository&amp;lt;MyEntity&amp;gt;&lt;/code&gt; creates a database entity with a new ID&lt;/li&gt;
&lt;li&gt;If your &lt;code&gt;cache&lt;/code&gt; and your actual database return data in the same format&lt;/li&gt;
&lt;li&gt;If sending a &lt;code&gt;POST&lt;/code&gt; request to some endpoint always returns the payload in a specific format&lt;/li&gt;
&lt;li&gt;If all instances of a &lt;code&gt;sealed class&lt;/code&gt; have their class' name on the &lt;code&gt;toString&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And much more scenarios that you might be exploring with!&lt;/p&gt;

</description>
      <category>kotest</category>
      <category>kotlin</category>
      <category>testing</category>
    </item>
    <item>
      <title>Handling fixed-length files using a Kotlin DSL</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Wed, 13 Nov 2019 12:34:36 +0000</pubDate>
      <link>https://dev.to/leocolman/handling-fixed-length-files-using-a-kotlin-dsl-1hm1</link>
      <guid>https://dev.to/leocolman/handling-fixed-length-files-using-a-kotlin-dsl-1hm1</guid>
      <description>&lt;p&gt;TL;DR - Fixed Length files can be troublesome to handle, and even more troublesome when there are multiple kinds of records on the lines. To solve this, we'll use &lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler" rel="noopener noreferrer"&gt;Guiabolso's fixed length file handler&lt;/a&gt;, a library designed just for this purpose.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hello Kotliners!
&lt;/h1&gt;

&lt;p&gt;In this article we will see a little bit on how to handle fixed-length files by using &lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler" rel="noopener noreferrer"&gt;this library, by Guiabolso&lt;/a&gt; (with some contributions made by myself). There are many solutions for this kind of problem on the JVM, but all of them are focused on Java.&lt;/p&gt;

&lt;p&gt;These libraries are old and not optimized for Kotlin, and become very cumbersome/verbose to use. We created a Kotlin-DSL to handle these cases in a beautiful and concise way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remembering: What is a Fixed-Length file?
&lt;/h3&gt;

&lt;p&gt;If you ever worked with a fixed length file, you know that most of the times it's a big pain in the ass.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xkj7507bqa7lwem5oxz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xkj7507bqa7lwem5oxz.jpg" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;em&gt;Photo by Tim Gouw from Pexels&lt;/em&gt;
&lt;/h6&gt;

&lt;p&gt;A fixed length or fixed width file is a file containing data separated by fields. These fields have a specific length and is sometimes prefixed/suffixed with a character to denote emptiness, such as &lt;code&gt;0003&lt;/code&gt; for an int &lt;code&gt;3&lt;/code&gt; of fixed length 4.&lt;/p&gt;

&lt;p&gt;An example of a fixed length file is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kotlin    1201.63
Java      0219.52
Python    0129.62
Javascript0308.43
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It represents data from &lt;a href="http://pypl.github.io/PYPL.html" rel="noopener noreferrer"&gt;PYPL&lt;/a&gt;, and the data is organized as:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;From index&lt;/th&gt;
&lt;th&gt;To Index&lt;/th&gt;
&lt;th&gt;Padding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Language Name&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;RightPadding(' ')&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ranking&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;LeftPadding('0')&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share %&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;LeftPadding('0')&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This kind of file is broadly used by legacy systems (I'm looking at you, banking system), and integrating with them is troublesome and not fun.&lt;/p&gt;
&lt;h3&gt;
  
  
  Parsing fixed-length files
&lt;/h3&gt;

&lt;p&gt;These files are hard to understand and boring to deal with. The solution to parsing them usually involves a lot of string manipulation and manual buffer control (or bringing the entire file to memory if it's viable).&lt;/p&gt;

&lt;p&gt;When dealing with Kotlin code, this verbosity is annoying. We want simplicity and conciseness.&lt;/p&gt;

&lt;p&gt;For this, the company I work for, Guiabolso, developed a small library for fixed-length file handling in Kotlin.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/GuiaBolso" rel="noopener noreferrer"&gt;
        GuiaBolso
      &lt;/a&gt; / &lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler" rel="noopener noreferrer"&gt;
        fixed-length-file-handler
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Handlers for Fixed Length files in a beautiful Kotlin DSL
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;This library provides a beautiful &lt;del&gt;(I wrote it, so I can say it's beautiful, right?)&lt;/del&gt; Kotlin DSL to parse this kind of file. Using our previous example as example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PYPLRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;langName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pyplSequence&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fixedLengthFileParser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PYPLRecord&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;PYPLRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'0'&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;This will allow us to map our file to a lazy &lt;code&gt;Sequence&lt;/code&gt;, which will process the file as a stream instead of bringing it to memory. The library already supports many of the usual Java/Kotlin types, without having to cast and translate them.&lt;/p&gt;
&lt;h3&gt;
  
  
  "Advanced" fixed-length files
&lt;/h3&gt;

&lt;p&gt;For some reason yet to be defined reason, some of these legacy systems use the same file for more than one record type&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81on84avmhe19loj57xn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81on84avmhe19loj57xn.jpg" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  Photo by Juan Pablo Serrano Arenas from Pexels
&lt;/h6&gt;

&lt;p&gt;In these cases, our example above will be used for more things, such as Developer Name and Preferred Language&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1Kotlin    1201.63
1Java      0219.52
1Python    0129.62
1Javascript0308.43
2Leonardo Colman LopesKotlin
2Jane Doe             Javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The type of the record is marked at some position in the line, and your system must find a way to parse it any way.&lt;/p&gt;

&lt;p&gt;This leads to a bigger String manipulation spaghetti and a more unsustainable code.&lt;/p&gt;

&lt;p&gt;The library also provides a way to parse this kind of file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PYPLRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;langName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;DevRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preferredLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fixedLengthFileParser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;fileInputStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;withRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'1'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;PYPLRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'0'&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="nf"&gt;withRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'2'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;DevRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PaddingRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;We believe that parsing fixed-length files will be easier with this library, and we hope to help anyone that needs this kind of feature. Take a look!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/GuiaBolso" rel="noopener noreferrer"&gt;
        GuiaBolso
      &lt;/a&gt; / &lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler" rel="noopener noreferrer"&gt;
        fixed-length-file-handler
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Handlers for Fixed Length files in a beautiful Kotlin DSL
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Fixed Length File Handler&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/GuiaBolso/fixed-length-file-handler/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://github.com/GuiaBolso/fixed-length-file-handler/blob/master/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/523b713b2e0ae9cbf58a1fedc54c1086ed11a3fc84c96e5a5bb81fa5aa7862b3/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f47756961426f6c736f2f66697865642d6c656e6774682d66696c652d68616e646c6572" alt="GitHub"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0abfb6eb85925a2f7bea3e68b34eb3ffc6b6df42734f7340e03329de260e0abe/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f62722e636f6d2e67756961626f6c736f2f46697865644c656e67746846696c6548616e646c6572"&gt;&lt;img src="https://camo.githubusercontent.com/0abfb6eb85925a2f7bea3e68b34eb3ffc6b6df42734f7340e03329de260e0abe/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f62722e636f6d2e67756961626f6c736f2f46697865644c656e67746846696c6548616e646c6572" alt="Maven Central"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Introduction&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;When processing data from some systems (mainly legacy ones), it's usual to have Fixed Length Files, which are files that contain lines which content is split using a specific length for each field of a record.&lt;/p&gt;
&lt;p&gt;This kind of files are sometimes tricky to handle as many times there is a spaghetti of string manipulations and padding, and character counting and... Well, many things to take care of.&lt;/p&gt;
&lt;p&gt;This library comes to the rescue of programmers dealing with fixed length files. It enables you to simply define how your records are structured and it will handle these records for you in a nice Kotlin DSL for further processing.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Using with Gradle&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Import it into your dependencies:&lt;/p&gt;
&lt;div class="highlight highlight-source-kotlin notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;dependencies {
    implementation(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;br.com.guiabolso:FixedLengthFileHandler:{version}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)
}&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Basic Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The basic usage assumes that you're reading a file with a single type of record.&lt;/p&gt;
&lt;p&gt;Given a Fixed-Length File:&lt;/p&gt;
&lt;div class="markdown-heading"&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/GuiaBolso/fixed-length-file-handler" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>kotlin</category>
      <category>fixedlength</category>
      <category>file</category>
      <category>library</category>
    </item>
    <item>
      <title>Testing Koin applications with Kotest</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Mon, 22 Jul 2019 15:09:16 +0000</pubDate>
      <link>https://dev.to/kotest/testing-koin-applications-with-kotlintest-1iip</link>
      <guid>https://dev.to/kotest/testing-koin-applications-with-kotlintest-1iip</guid>
      <description>&lt;p&gt;TL;DR - Using the &lt;code&gt;KoinListener&lt;/code&gt; from &lt;a href="https://github.com/kotest/kotest" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt; to write tests that depend on &lt;a href="https://insert-koin.io/" rel="noopener noreferrer"&gt;Koin&lt;/a&gt; modules in a Kotest Spec&lt;/p&gt;

&lt;h1&gt;
  
  
  Hello, Kotliners!
&lt;/h1&gt;

&lt;p&gt;In this article we will see how to test applications that use the &lt;a href="https://insert-koin.io/" rel="noopener noreferrer"&gt;Koin DI Framework&lt;/a&gt; in a &lt;code&gt;Spec&lt;/code&gt; from the &lt;a href="https://github.com/kotest/kotest" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt; Framework. We'll take the JUnit example from Koin's documentation and enhance it with Kotest.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 simple steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add Kotest's Koin extensions to your build.gradle&lt;/li&gt;
&lt;li&gt;Transform your JUnit test into a Kotest Spec (or create one from scratch)&lt;/li&gt;
&lt;li&gt;Add the KoinListener to your class&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Add Koin extension to your build.gradle
&lt;/h2&gt;

&lt;p&gt;On your &lt;code&gt;build.gradle&lt;/code&gt; file we'll add the Koin Extension dependency as a &lt;code&gt;testImplementation&lt;/code&gt;. I'm assuming you already have Kotest configured. If you don't, &lt;a href="https://github.com/kotest/kotest#use" rel="noopener noreferrer"&gt;take a look at the docs&lt;/a&gt; on how to do it.&lt;/p&gt;




&lt;p&gt;I like to keep the Koin Extension definition close to my Kotest definition&lt;/p&gt;

&lt;p&gt;build.gradle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    // ... Your dependencies ... 

    // Kotest
    testImplementation("io.kotest:kotest-runner-junit5:{version}")
    testImplementation("io.kotest:kotest-extensions-koin:{version}")

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Transform a JUnit test to a Kotest Spec
&lt;/h2&gt;

&lt;p&gt;For this example, we are going to use the &lt;a href="https://insert-koin.io/docs/2.0/quick-references/testing-your-components/" rel="noopener noreferrer"&gt;JUnit example from the Koin docs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyTest : KoinTest {

    // Lazy inject property
    val componentA : ComponentA by inject()

    // use it in your tests :)
    @Test
    fun `make a test with Koin`() {
        startKoin { modules(appModule) }

        // use componentA here!
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting the test
&lt;/h3&gt;

&lt;p&gt;There are a &lt;a href="https://github.com/Kotest/Kotest/blob/master/doc/styles.md" rel="noopener noreferrer"&gt;lot of styles&lt;/a&gt; that can be used in Kotest, let's try with &lt;code&gt;FunSpec&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyTest : FunSpec(), KoinTest {


    // Lazy inject property
    val componentA : ComponentA by inject()

    init {
        test("Make a test with Koin") {
            startKoin { modules(appModule) }

            // use componentA here!
        }
    }

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

&lt;/div&gt;



&lt;p&gt;This will already work! &lt;/p&gt;

&lt;p&gt;This example works in the same way as the JUnit code. If we add more tests to the class, we'll have to clear Koin's context. In JUnit we create a &lt;code&gt;@After&lt;/code&gt; method. Let's see how to do this in Kotest&lt;/p&gt;




&lt;h2&gt;
  
  
  Add KoinListener to the class
&lt;/h2&gt;

&lt;p&gt;To fix this, we can either override the &lt;code&gt;afterTest&lt;/code&gt; function, that works similarly to &lt;code&gt;@After&lt;/code&gt; or we can use the &lt;code&gt;KoinListener&lt;/code&gt; to do that automatically for us! Let's try with that approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyTest : FunSpec(), KoinTest {

    override fun listeners() = listOf(KoinListener(appModule))

    // Lazy inject property
    val componentA : ComponentA by inject()

    init {
        test("Make a test with Koin") {

            // use componentA here!
        }
    }

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

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;KoinListener&lt;/code&gt; we will have Kotest handle both Koin's start (see that we removed that line in our test!) and Koin's stop.&lt;/p&gt;

&lt;h3&gt;
  
  
  And that's it!
&lt;/h3&gt;

&lt;p&gt;With this simple setup, you'll be able to test your Koin Application with Kotest, and use all the available features from both of them!&lt;/p&gt;

&lt;p&gt;If you still have any doubt, check out the &lt;a href="https://github.com/Kotest/Kotest/blob/master/doc/extensions.md#koin" rel="noopener noreferrer"&gt;full documentation&lt;/a&gt; on &lt;code&gt;KoinListener&lt;/code&gt;&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotest</category>
      <category>testing</category>
      <category>koin</category>
    </item>
    <item>
      <title>Testing System Properties and System Environment with Kotest</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Sun, 21 Jul 2019 21:16:50 +0000</pubDate>
      <link>https://dev.to/kotest/testing-system-properties-and-system-environment-with-kotlintest-3cnb</link>
      <guid>https://dev.to/kotest/testing-system-properties-and-system-environment-with-kotlintest-3cnb</guid>
      <description>&lt;p&gt;TL;DR: Test code that uses &lt;code&gt;System.getenv()&lt;/code&gt; or &lt;code&gt;System.getProperty()&lt;/code&gt; with easy-to-use functions with &lt;a href="https://github.com/Kotest/Kotest" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Hello Kotliners!
&lt;/h1&gt;

&lt;p&gt;In this article we'll discuss a little bit two features of Kotest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System Environment extensions&lt;/li&gt;
&lt;li&gt;System Property extensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're very similar on their implementation and use, and are very easy to use!&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;In the JVM world, some (many) times our code depends on what the system provides us, usually through &lt;em&gt;Environment Variables&lt;/em&gt; or &lt;em&gt;Properties&lt;/em&gt; passed to the JVM.&lt;/p&gt;

&lt;p&gt;However, neither is very easy to test when we want to use it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;System.getenv()&lt;/code&gt; map &lt;a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/solaris/classes/java/lang/ProcessEnvironment.java#l78" rel="noopener noreferrer"&gt;is immutable&lt;/a&gt;. We can't set it up using plain Java&lt;/li&gt;
&lt;li&gt;Messing with &lt;code&gt;System.setProperty()&lt;/code&gt; manually forces us to do all the setup and tear-down, and becomes very verbose and cumbersome&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://unsplash.com/@vheath" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FtaONeg2.jpg" width="800" height="333"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Photo by &lt;a href="https://unsplash.com/@vheath?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Victoria Heath&lt;/a&gt; on Unsplash&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;Kotest provides some extensions and listeners to handle these scenarios. The complete documentation can be found &lt;a href="https://github.com/Kotest/Kotest/blob/master/doc/extensions.md#system-extensions" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  System Environment Extensions
&lt;/h3&gt;

&lt;p&gt;If the piece of code under test is small enough and used only once, we can use the &lt;code&gt;withSystemEnvironment&lt;/code&gt;. It has overloads, but we'll use the &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; one. There's an overload expecting a &lt;code&gt;Pair&amp;lt;K,V&amp;gt;&lt;/code&gt; and one expecting a &lt;code&gt;Map&amp;lt;K,V&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changing environment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;withSystemEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"barValue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKey"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"barValue"&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;h3&gt;
  
  
  System Properties Extensions
&lt;/h3&gt;

&lt;p&gt;In the same fashion as above, we have the function &lt;code&gt;withSystemProperties&lt;/code&gt;, that works very similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PropertyTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changing System Properties"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;withSystemProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKey"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"barValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"booKey"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"batValue"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKey"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"barValue"&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"booKey"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"batValue"&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;h3&gt;
  
  
  Cleanup
&lt;/h3&gt;

&lt;p&gt;After the execution, the previous values will be set to what they were before. What is already in the map will remain untouched during the execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Listeners
&lt;/h3&gt;

&lt;p&gt;Perhaps your code require the specific properties in every test. You can configure the &lt;code&gt;SystemPropertyTestListener&lt;/code&gt; and the &lt;code&gt;SystemEnvironmentTestListener&lt;/code&gt; for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemEnvironmentTestListenerTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;SystemEnvironmentTestListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKeyEnv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"barValueEnv"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;SystemPropertyTestListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKeyProp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"barValueProp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"System Property"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKeyProp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"barValueProp"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"System Environment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fooKeyEnv"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"barValueEnv"&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;The original values will reset after the test execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Listener
&lt;/h3&gt;

&lt;p&gt;You can also set these globally by using the &lt;code&gt;SystemEnvironmentProjectListener&lt;/code&gt; and the &lt;code&gt;SystemPropertyProjectListener&lt;/code&gt;. More information on Project Configuration can be found in &lt;a href="https://github.com/Kotest/Kotest/blob/master/doc/reference.md#project-config" rel="noopener noreferrer"&gt;Kotest's Docs&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectConfig&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AbstractProjectConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TestListener&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SystemEnvironmentProjectListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Override Mode
&lt;/h3&gt;

&lt;p&gt;All the above extensions accept a parameter of the enum &lt;code&gt;OverrideMode&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SetOrOverride - If the key we want to set is already in the properties/environment, we want to override it for the test&lt;/li&gt;
&lt;li&gt;SetOrIgnore - If the key is already present, we'll not override it and the test will continue&lt;/li&gt;
&lt;li&gt;SetOrError (default) - If the key is already present, interrupt the test with an Exception&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are very useful if you need to use, for example, a property in the global listener, but want to override it in a specific test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;withSystemProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OverrideMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetOrOverride&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// foo is overrided here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extensions in action
&lt;/h3&gt;

&lt;p&gt;I've published 2 small and simple libs that exercise these listeners and extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Kerooker/SimpleProperties" rel="noopener noreferrer"&gt;SimpleProperties&lt;/a&gt; - That reads values both from Environment and Properties. &lt;a href="https://github.com/Kerooker/SimpleProperties/blob/master/src/test/kotlin/com/kerooker/simpleproperties/internal/profile/SystemProfileLoaderTest.kt" rel="noopener noreferrer"&gt;Test Class&lt;/a&gt;. It's even possible to see both extensions &lt;a href="https://github.com/Kerooker/SimpleProperties/blob/a5650444029050a80f5fdc6128a45f73f8a299b6/src/test/kotlin/com/kerooker/simpleproperties/internal/profile/SystemProfileLoaderTest.kt#L53" rel="noopener noreferrer"&gt;used at once&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Kerooker/SimpleFeatureThrottler" rel="noopener noreferrer"&gt;SimpleFeatureThrottler&lt;/a&gt; - A similar approach can be seen in &lt;a href="https://github.com/Kerooker/SimpleFeatureThrottler/blob/master/src/test/kotlin/com/kerooker/simplefeaturethrottler/internal/SystemPercentageFetcherTest.kt" rel="noopener noreferrer"&gt;this test class&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>kotest</category>
      <category>testing</category>
    </item>
    <item>
      <title>Testing a Spring Boot application with Kotest</title>
      <dc:creator>Leonardo Colman Lopes</dc:creator>
      <pubDate>Thu, 11 Jul 2019 03:49:44 +0000</pubDate>
      <link>https://dev.to/kotest/testing-a-spring-boot-application-with-kotlintest-pgd</link>
      <guid>https://dev.to/kotest/testing-a-spring-boot-application-with-kotlintest-pgd</guid>
      <description>&lt;p&gt;TL;DR - How to use the SpringListener from &lt;a href="https://github.com/Kotest/Kotest" rel="noopener noreferrer"&gt;Kotest&lt;/a&gt; to test Spring Boot applications, including some samples&lt;/p&gt;

&lt;h1&gt;
  
  
  Hello Kotliners!
&lt;/h1&gt;

&lt;p&gt;In this article we'll see how to test Spring Boot apps using the &lt;a href="https://github.com/Kotest/Kotest" rel="noopener noreferrer"&gt;Kotest Framework&lt;/a&gt;. We'll see some examples on how we test things using JUnit, and how to easily move to Kotest and enjoy it's features.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 simple steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add Kotest's Spring Extension to your &lt;code&gt;build.gradle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Transform your JUnit test into Kotest format (or create a new test from scratch)&lt;/li&gt;
&lt;li&gt;Add the SpringListener to your class&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Add Kotest Spring Extension
&lt;/h2&gt;

&lt;p&gt;On your &lt;code&gt;build.gradle&lt;/code&gt; file we'll add the Spring Extension dependency as a &lt;code&gt;testImplementation&lt;/code&gt;. I'm assuming you already have Kotest configured. If you don't, &lt;a href="https://github.com/Kotest/Kotest#use" rel="noopener noreferrer"&gt;take a look at the docs&lt;/a&gt; on how to do it.&lt;/p&gt;




&lt;p&gt;I like to keep the Spring Extension definition close to my Kotest definition&lt;/p&gt;

&lt;p&gt;build.gradle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    // ... Your dependencies ... 

    // Kotest
    testImplementation("io.kotest:kotest-runner-junit5:{version}")
    testImplementation("ioio.kotest.extensions:kotest-extensions-spring:{version}")

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://search.maven.org/artifact/io.kotest.extensions/kotest-extensions-spring" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fmaven-central%2Fv%2Fio.kotest.extensions%2Fkotest-extensions-spring.svg%3Flabel%3Dlatest%2520release" width="130" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Transform a JUnit test to a Kotest Spec
&lt;/h2&gt;

&lt;p&gt;For this part we'll assume that we already have a component in our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bar"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes yes, it's very simple and very dummy... But stay with me, we will go step by step!&lt;/p&gt;

&lt;p&gt;We usually write a JUnit + Spring integrated test for this, validating that this bean works correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.baeldung.com/spring-boot-testing" rel="noopener noreferrer"&gt;There&lt;/a&gt;.&lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html" rel="noopener noreferrer"&gt;are&lt;/a&gt;.&lt;a href="https://spring.io/guides/gs/testing-web/" rel="noopener noreferrer"&gt;many&lt;/a&gt;.&lt;a href="https://reflectoring.io/unit-testing-spring-boot/" rel="noopener noreferrer"&gt;guides&lt;/a&gt; that show us how to do it. Let's take this basic approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RunWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringRunner&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponentTest&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponent&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fooShouldReturnBar&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&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;h2&gt;
  
  
  Transform that test in a Kotest Spec
&lt;/h2&gt;

&lt;p&gt;There are a &lt;a href="https://github.com/Kotest/Kotest/blob/master/doc/styles.md" rel="noopener noreferrer"&gt;lot of styles&lt;/a&gt; that can be used in Kotest, let's try with &lt;code&gt;FunSpec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's convert it exactly as is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RunWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringRunner&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponentTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponent&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo should return Bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"Bar"&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;Aaaand our test crashes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;kotlin.UninitializedPropertyAccessException: lateinit property simpleComponent has not been initialized&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's ok, it's ok! We need to tell Kotest that this is a Spring Test!&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the SpringListener to the class
&lt;/h2&gt;

&lt;p&gt;To fix the above error, we need to add the &lt;code&gt;SpringListener&lt;/code&gt; to our class' listeners:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponentTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TestListener&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringListener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SimpleComponent&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo should return Bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;simpleComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"Bar"&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;Notice that we also removed the &lt;code&gt;@RunWith(SpringRunner::class)&lt;/code&gt;, as we are not running with JUnit anymore.&lt;/p&gt;

&lt;h4&gt;
  
  
  And &lt;em&gt;voilà&lt;/em&gt;, it works!
&lt;/h4&gt;

&lt;p&gt;Of course this is a very modest example. What about all the other configurations? Profiles, context configuration, TestConfiguration?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don't worry!&lt;/em&gt; they'll work the same way as we're used to in JUnit. Annotating the constructor and such.&lt;/p&gt;

&lt;p&gt;For example, messing with profiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Components&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@ActiveProfiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-profile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActiveProfileSpringTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FunSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringListener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\${test-foo}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;testFoo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should load active profile properties correctly"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;testFoo&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"bar"&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;&lt;a href="https://github.com/kotest/kotest-extensions-spring/blob/master/src/test/kotlin/io/kotest/extensions/spring/ActiveProfileSpringTest.kt" rel="noopener noreferrer"&gt;test from Kotest test suite&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;And this is all we have for today! Go ahead and test your Spring Apps with Kotest! More information on this extension can be found at &lt;a href="https://kotest.io/docs/extensions/spring.html" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doubts? Suggestions? Leave us a comment!&lt;/p&gt;

</description>
      <category>kotest</category>
      <category>kotlin</category>
      <category>testing</category>
      <category>spring</category>
    </item>
  </channel>
</rss>
