<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Aliaksandr Rasolka on Medium]]></title>
        <description><![CDATA[Stories by Aliaksandr Rasolka on Medium]]></description>
        <link>https://medium.com/@rosolko?source=rss-dbc509da9dfd------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Qm_L3DfLEuY--pt5tLyTXA.png</url>
            <title>Stories by Aliaksandr Rasolka on Medium</title>
            <link>https://medium.com/@rosolko?source=rss-dbc509da9dfd------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 09 Apr 2026 00:11:59 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rosolko/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Block an advertisements for application under tests]]></title>
            <link>https://medium.com/@rosolko/block-an-advertisements-for-application-under-tests-52ea468ec288?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/52ea468ec288</guid>
            <category><![CDATA[firefox]]></category>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[tracking]]></category>
            <category><![CDATA[ublock-origin]]></category>
            <category><![CDATA[advertising]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Mon, 10 Jun 2019 19:20:49 GMT</pubDate>
            <atom:updated>2019-06-10T19:56:11.076Z</atom:updated>
            <content:encoded><![CDATA[<p>No magic, just a profile with configs and extension…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tPV_MA8WHbYGGwUM" /><figcaption>Photo by <a href="https://unsplash.com/@wojtek?utm_source=medium&amp;utm_medium=referral">Wojtek Witkowski</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Imagine — you receive a task when you need to test application. Cool, sound simple, but what if it contains a lot of advertisements? What if ads going to loads and shown dynamically on your page?</p><p>You automation is going to be an unstable in this conditions. Reasonable question — what can I do this case?</p><p>I see few solutions:</p><ol><li>Setup you test application not to display ads at all. Sounds simple, but there is some cases. For first not all environment support ads disabling. For second — it can be some third-party or even better — an external (test task) environment.</li><li>Use some proxy, intercept and filter traffic. This case you need to integrate with some proxy, setup test environment, create a rules, apply them in a driver proxy. Sounds quite complex isn’t it?</li><li>Just create a custom profile, setup it, install some ad-block extension, export and use this profile to driver. Again a lot of steps, but stop… they all simple. No code for ads blocking or traffic filtering. Cool, let’s go this way!</li></ol><p>Let’s start our journey. First, let choose some browser — let it be a Firefox.</p><p>As I sad, until driver configuration we will no code at all.</p><p>Step-by-step:</p><ol><li>Start a browser as regular user</li><li>Open a profile manager by typing about:profiles</li><li>Create a new profile, let’s name it automation — CO</li><li>Launch profile in new browser to set-up and install all things to fight against ads</li><li>This step you can configure browser as you want — all setting will be saved in a profile folder</li><li>And finally install an ad blocker, set-up it, add filters and update them.</li><li>Save profile by closing opened before browser window</li><li>Next I copy profile folder (you can find it in about:profiles&gt; Root Directory section of your new profile) to my resource directory. Folder will have name something like — x0fh1by2.automation</li><li>Final step required code a little bit to make this works</li></ol><p>Now let’s make the code work for you.</p><p>First we need to create a firefox profile using previously created profile for this we need to load folder as a File:</p><pre>File profilePath = Paths.<em>get</em>(<strong>&quot;src&quot;</strong>, <strong>&quot;test&quot;</strong>, <strong>&quot;resources&quot;</strong>, <strong>&quot;x0fh1by2.automation&quot;</strong>).toFile();</pre><p>Then create a firefox profile based on loaded profile:</p><pre>FirefoxProfile profile = new FirefoxProfile(profilePath);</pre><p>To create a firefox driver with firefox profile we need to create a firefiox options and set profile to:</p><pre>FirefoxOptions options = new FirefoxOptions();<br>options.setProfile(profile);</pre><p>Finally we can create a firefox driver:</p><pre>WebDriver driver = new FirefoxDriver(options);</pre><p>And of course test how it works by open any site with some trackers or ads — let it be some local news site:</p><pre>driver.get(<strong>&quot;https://local-news-site.net&quot;</strong>);</pre><p>For clarity, first I will open it with a standard driver, and then with custom profile.</p><p>Let’s compare driver work with <em>Standard vs Custom </em>profiles!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F8iYQ8pJHP0FghPmZUgOAw.png" /><figcaption>Standard profile</figcaption></figure><p><em>Standard</em>:</p><p>Here you can see a lot of ads… Content almost not visible because of it. Also ads take some time, traffic and resources to loads on a page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*nrNaD0z2oOhEV8tMAhtbXw.png" /><figcaption>Custom profile</figcaption></figure><p><em>Custom</em>:</p><p>No ads because we apply [RU] specific filter on profile/extension configuration step. All ads an trackers blocked.</p><p>Page loads much… much faster!!1</p><p>In my opinion — an amazing result. But here is a dark side — for loading custom profile you need extra time instead of using a default one.</p><p>On this note we will round up, meet again another day, another time! No repository this time, because all code placed in article — as I sad almost no code.</p><p>Happy coding and testing! \ (•◡•) /</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=52ea468ec288" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Configure Selenide to work with Selenoid]]></title>
            <link>https://medium.com/@rosolko/configure-selenide-to-work-with-selenoid-8835cd6dc7d2?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/8835cd6dc7d2</guid>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[selenide]]></category>
            <category><![CDATA[selenoid]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[junit-5]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Tue, 28 May 2019 20:24:03 GMT</pubDate>
            <atom:updated>2019-05-29T06:47:57.377Z</atom:updated>
            <content:encoded><![CDATA[<p>In this story I will consider several integration options: very simple and simple :D</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tXp21Wt6kr4xnT8O" /><figcaption>Photo by <a href="https://unsplash.com/@barkiple?utm_source=medium&amp;utm_medium=referral">John Barkiple</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>First of all you need to setup and run Selenoid on your environment. It can be easily done follow official <a href="https://aerokube.com/selenoid/latest/">documentation</a>.</p><p>We going fast way, so let’s use <a href="http://aerokube.com/cm/latest/">Configuration Manager</a> for it.</p><pre>./cm selenoid start</pre><p>After some time as result we have Selenoid stated and it’s ready to run your tests against and tests endpoint is — <a href="http://localhost:4444/wd/hub">http://localhost:4444/wd/hub</a></p><p>Now Selenide’s turn. As I said before we going to look on two integration options. First — single line configuration.</p><p>Let’s create a test project and integrate, integrate, integrate… I will use <a href="https://github.com/gradle/gradle">Gradle</a> as a build tool and <a href="https://github.com/junit-team/junit5">JUnit5</a> as a testing framework.</p><p>Add JUni5 — testImplementation(&quot;org.junit.jupiter:junit-jupiter:5.4.2&quot;)</p><p>Add Selenide — testImplementation(&quot;com.codeborne:selenide:5.2.3&quot;)</p><p>In very simple case we just need to tell Selenide run tests against remote web driver by adding single line:</p><pre>Configuration.<em>remote </em>= <strong>&quot;http://localhost:4444/wd/hub&quot;</strong>;</pre><p>Also don’t forget to disable WebDriverManager:</p><pre>Configuration.<em>driverManagerEnabled </em>= false;</pre><p>And put it to test setup block:</p><pre>@BeforeEach<br>void setUp() {<br>  Configuration.driverManagerEnabled = false;<br>  Configuration.remote = &quot;http://localhost:4444/wd/hub&quot;;<br>}</pre><p>Create simple test and execute it:</p><pre>@Test                           <br>void ableToRunDefaultDriverOnSelenoid() {<br>  open(&quot;<a href="https://www.google.com">https://www.google.com</a>&quot;);<br>  assertEquals(title(), &quot;Google&quot;);<br>}</pre><p>As result in logs we can see that session was created inside Selenoid container as RemoteWebDriver:</p><blockquote>INFO: BrowserName=chrome Version=74.0.3729.157 Platform=LINUX<br>INFO: Create webdriver in current thread 1: RemoteWebDriver -&gt; RemoteWebDriver: chrome on LINUX (23b5bb5746fdc8cc16b7f850e9080092)</blockquote><p>Perfect!</p><p>But what if we need to create a custom WebDriver? And Selenide has such ability. It’s described in <a href="https://github.com/selenide/selenide/wiki/How-Selenide-creates-WebDriver#how-to-run-selenide-with-custom-profile">Selenide Wiki on GitHub</a>. In my case I just want to execute tests on Firefox and accept insecure certificates:</p><pre>public static class CustomProvider implements WebDriverProvider {<br>  <strong>@Override<br>  </strong>public WebDriver createDriver(DesiredCapabilities capabilities) {<br>    FirefoxOptions firefoxOptions = new FirefoxOptions();<br>    firefoxOptions.setAcceptInsecureCerts(true);<br>    firefoxOptions.merge(capabilities);<br>    try {<br>      return new RemoteWebDriver(new URL(<strong>&quot;http://localhost:4444/wd/hub&quot;</strong>), firefoxOptions);<br>    } catch (final MalformedURLException e) {<br>        throw new RuntimeException(<strong>&quot;Unable to create driver&quot;</strong>, e);<br>    }<br>  }<br>}</pre><p>Also don’t forget to disable WebDriverManager and set custom driver as browser to Selenide:</p><pre>@BeforeEach<br>void setUp() {<br>  Configuration.<em>driverManagerEnabled </em>= false;<br>  Configuration.<em>browser </em>= CustomProvider.class.getName();<br>}</pre><p>Easy as pie — save, run, relax:</p><blockquote>INFO: BrowserName=firefox Version=67.0 Platform=LINUX<br>INFO: Create webdriver in current thread 1: RemoteWebDriver -&gt; RemoteWebDriver: firefox on LINUX (e5397009-df23–4327–8d69-b031fc10fc3b)</blockquote><p>Happy coding and testing! (◕‿◕)</p><p>You can find complete project on GitHub.</p><p><a href="https://github.com/rosolko/selenide-selenoid-configuration">rosolko/selenide-selenoid-configuration</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8835cd6dc7d2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Publish java library to JFrog Bintray and Sonatype with Gradle]]></title>
            <link>https://medium.com/@rosolko/publish-java-library-to-jfrog-bintray-and-sonatype-with-gradle-1a3ebd5b8be8?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/1a3ebd5b8be8</guid>
            <category><![CDATA[maven-central]]></category>
            <category><![CDATA[bintray]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[jcenter]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Thu, 07 Mar 2019 13:47:43 GMT</pubDate>
            <atom:updated>2019-03-15T06:51:32.195Z</atom:updated>
            <content:encoded><![CDATA[<h3>Publish java library to JFrog Bintray and OSS Sonatype with Gradle</h3><p>Few steps but so much pain in the process…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wDaWaSdp97j-6AH4" /><figcaption>Photo by <a href="https://unsplash.com/@mischievous_penguins?utm_source=medium&amp;utm_medium=referral">Casey Horner</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Checklist:</p><ol><li>Create Bintray account</li><li>Modify build script</li><li>Publish to local repository and validate publication</li><li>Enable automatic signing on upload on Bintray repository</li><li>Publish artifact to Bintray</li><li>Synchronize publication with JCenter repository</li><li>Check availability</li><li>Create an account on Sonatype JIRA</li><li>Create ticket and wait until ticket will be resolved with <strong><em>“Central sync is activated for &lt;your space&gt;”</em></strong> reason</li><li>Wait for 4–5 hour or even better for 1 day</li><li>Synchronize publication with Maven Central repository</li><li>Check availability</li><li>Cheers!</li></ol><p>My story was started right after completion of small <a href="https://github.com/rosolko/wdm4j">library</a>. Some time after I ask myself — how do I distribute this library?</p><p>Based on my previous experience I prefer to <a href="https://jitpack.io">JitPack</a> because this way simple as much as possible.</p><p>Just find your library, modify you build script and use it. The problem is — additional repository in your build script. Not all people want to resolve one more additional repository in a target project.</p><p>The second problem — I should somehow care about versioning. This case I can only associate artifact version with branch, commit, or release.</p><p>As result I got a Gradle build script looks like:</p><pre>repositories {<br>  maven { url &#39;https://jitpack.io&#39; }<br>}</pre><pre>dependencies {<br>  implementation &#39;com.github.rosolko:wdm4j:master-SNAPSHOT&#39;<br>}</pre><p>Next I’m trying to use a <a href="https://bintray.com">JFrog Bintray</a> to publish artifacts (bonus of this option — ability to sync artifacts with maven central repository). Registration with my <a href="https://github.com/rosolko">GitHub account</a> allows me to create a <a href="https://bintray.com/rosolko">free account</a> for open source using. Right after — create a repository, let’s call it <strong>maven</strong> with <em>Maven</em> type. For now — that’s it.</p><p>This case we need to modify our build script, so — let’s do it!</p><p>First of all we need to add <strong>maven-publish </strong>plugin and define several fields to successfully publish valid artifacts. Deep documentation you can find in Gradle <a href="https://docs.gradle.org/current/userguide/publishing_maven.html">publish documentation</a>.</p><pre>apply plugin: &quot;maven-publish&quot;</pre><p>Next register tasks that build and collect artifacts:</p><pre>tasks.register(&quot;sourcesJar&quot;, Jar) {<br>    from sourceSets.main.allJava<br>    classifier &quot;sources&quot;<br>}<br><br>tasks.register(&quot;javadocJar&quot;, Jar) {<br>    from javadoc<br>    classifier &quot;javadoc&quot;<br>}</pre><p>Next to we need to create a publishing:</p><pre>publishing {<br>    publications {<br>        mavenJava(MavenPublication) {<br>            from components.java<br>            artifact sourcesJar<br>            artifact javadocJar<br>            pom {<br>                name = &quot;wdm4j&quot;<br>                description = &quot;WebDriver binary manager for java&quot;<br>                url = &quot;https://github.com/rosolko/wdm4j&quot;<br>                licenses {<br>                    license {<br>                        name = &quot;MIT&quot;<br>                        url = &quot;https://github.com/rosolko/wdm4j/blob/master/LICENSE&quot;<br>                    }<br>                }<br>                developers {<br>                    developer {<br>                        id = &quot;rosolko&quot;<br>                        name = &quot;Aliaksandr Rasolka&quot;<br>                        email = &quot;rosolko@gmail.com&quot;<br>                    }<br>                }<br>                scm {<br>                    connection = &quot;scm:git:https://github.com/rosolko/wdm4j.git&quot;<br>                    developerConnection = &quot;scm:git:https://github.com/rosolko/wdm4j.git&quot;<br>                    url = &quot;https://github.com/rosolko/wdm4j&quot;<br>                }<br>            }<br>        }<br>    }<br>}</pre><p>Right after that we configure bintray:</p><pre>bintray {<br>    user = project.hasProperty(&#39;bintrayUser&#39;) <br>    key = project.hasProperty(&#39;bintrayApiKey&#39;)<br>    publications = [&#39;mavenJava&#39;]<br>    pkg {<br>        repo = &#39;maven&#39;<br>        name = &#39;wdm4j&#39;<br>        vcsUrl = &#39;https://github.com/rosolko/wdm4j.git&#39;<br>        licenses = [&#39;MIT&#39;]<br>    }<br>}</pre><p>After that we can execute <strong>publishToMavenLocal</strong> task, check that we have valid artifacts, and perform <strong>bintrayUpload</strong> task to upload.</p><p>As result <a href="https://bintray.com/rosolko/maven/wdm4j">library</a> should appear in previously created <a href="https://bintray.com/rosolko/maven">repository</a>. And of cource we receive an ability to link library with <a href="https://bintray.com/bintray/jcenter?filterByPkgName=wdm4j">jcenter</a>.</p><p>After some indexing time it will appear and available for use:</p><pre>repositories {<br>  jcenter<br>}</pre><pre>dependencies {<br>  implementation &#39;com.github.rosolko:wdm4j:1.0.0&#39;<br>}</pre><p>On this step we can stop, but bintray has an ability to sync artifacts with maven central repository, why not. Let’s set this up.</p><p>On this step problems will starts. For first you can’t easily create account and publish something.</p><p>For this sonatype have a special <a href="https://central.sonatype.org/pages/ossrh-guide.html">guide</a>, short list:</p><ol><li>Create JIRA account for <a href="https://issues.sonatype.org">sonatype</a></li><li>Open an <a href="https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&amp;pid=10134">issue</a> to create your space for library upload</li><li>Sign your artifacts</li><li>Upload</li></ol><p>If with first two points there are no problem — signing can be quite tricky, but bintray allows you to configure repository with automatic GPG signing uploaded files using Bintray’s <a href="https://bintray.com/user/downloadSubjectPublicKey?username=bintray">public</a>/private key pair.</p><p>Right after that upload your artifacts again and sync library repository with <a href="https://bintray.com/package/central/rosolko/maven/wdm4j">Maven Central</a>.</p><p>For this operation you also need <a href="https://help.sonatype.com/repomanager2/configuration/security-setup-with-user-tokens#SecuritySetupwithUserTokens-EnablingandResettingUserTokens">key and token</a>. Sync, release and repository when sync done and wait.</p><blockquote>Note: The responsiveness of the Maven Central gateway at Sonatype fluctuates. This operation may take several minutes depending on the weather.</blockquote><p>As result you library will appear on <a href="https://search.maven.org/artifact/com.github.rosolko/wdm4j/1.0.0/jar">maven search portal</a> and you can use it in your build script without modifying your existing repository list:</p><pre>repositories {<br>  mavenCentral()<br>}</pre><pre>dependencies {<br>  implementation &#39;com.github.rosolko:wdm4j:1.0.0&#39;<br>}</pre><p>Yay, quest complete, you are awesome ~(* — *)~</p><p>Complete build scripts and library itself you can easily find on GitHub:</p><p><a href="https://github.com/rosolko/wdm4j">rosolko/wdm4j</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a3ebd5b8be8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TIL 5: Made your retrofit rest client reactive a little bit.]]></title>
            <link>https://medium.com/@rosolko/til-5-made-your-retrofit-rest-client-a-little-reactive-b054072785c5?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/b054072785c5</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <category><![CDATA[async]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[automation-testing]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 06 Jul 2018 13:02:41 GMT</pubDate>
            <atom:updated>2019-05-28T20:14:29.102Z</atom:updated>
            <content:encoded><![CDATA[<p>Just apply right adapter.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*UQpkZOwRyq8yThlB" /><figcaption>Photo by <a href="https://unsplash.com/@helloimnik?utm_source=medium&amp;utm_medium=referral">Hello I’m Nik</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Day-to-day I’m using Retrofit2 client and all going flawless until I start using call asynchronously and try to chain operations.</p><p>First chapter.</p><p>So, what I have on start?</p><p>API client:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc50a762355ceda60d9ecc0063b90d2c/href">https://medium.com/media/bc50a762355ceda60d9ecc0063b90d2c/href</a></iframe><p>And service that implement this interface:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/825707cc16281d7f514177c964db36c4/href">https://medium.com/media/825707cc16281d7f514177c964db36c4/href</a></iframe><p>And use it:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0d2d83e31cdf189fc756055ab536d93d/href">https://medium.com/media/0d2d83e31cdf189fc756055ab536d93d/href</a></iframe><p>Nothing special but it works. And works well.</p><p>Unfortunately new logic required some changes.</p><p>Second chapter.</p><p>What if you need to login and execute 2 tasks in parallel using access token from login method? This case I decide to use CompletableFuture from Java 8.</p><p>On this level I modify only test logic:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/49a5e0df8de94f257e1a4481142448ee/href">https://medium.com/media/49a5e0df8de94f257e1a4481142448ee/href</a></iframe><p>So, easy using, not much code changes… but with this implementation I should wrap each standalone future with CompletableFuture and pass lambda with function that execute API call.</p><p>Weird. Let’s find some cool solution.</p><p>Third chapter.</p><p>Search a little… and more… boo — official <a href="https://github.com/square/retrofit/tree/master/retrofit-adapters/java8">implementation</a> with documentation :D</p><p>Let’s tune this code out. Since I use Gradle — apply required script and fetch library:</p><pre>implementation(<strong>&quot;com.squareup.retrofit2:adapter-java8:2.4.0&quot;</strong>)</pre><p>Register adapter to your Retrofit:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ef5ffe0d33b943c1b7d8f53d4f232673/href">https://medium.com/media/ef5ffe0d33b943c1b7d8f53d4f232673/href</a></iframe><p>Tune API client to return ready CompletableFutures:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/02500289647e1e7ab9ba6dddae169cfa/href">https://medium.com/media/02500289647e1e7ab9ba6dddae169cfa/href</a></iframe><p>Modify your service with future workaround. This place you can even handle result and remove try/catch construction:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5675d414ff358fdffa34eb19a5400c0e/href">https://medium.com/media/5675d414ff358fdffa34eb19a5400c0e/href</a></iframe><p>Finally let’s change out test:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1c50dbca92db045e27f28a8fac594af4/href">https://medium.com/media/1c50dbca92db045e27f28a8fac594af4/href</a></iframe><p>As you can see for direct API call no need to pass functions — awesome.</p><p>Now you just need to worry about chain calls :D</p><p>Finally cut out results:</p><ol><li>All API calls async by default.</li><li>Call will be executed only when you access future data any way. For example future.get() or future.join() methods calls.</li><li>No need to think about wrapping method with functions in tests.</li><li>Easily result handling.</li><li>Less code. A little bit :D</li></ol><p>Happy coding and testing! #_#</p><h3>Previous story:</h3><p><a href="https://medium.com/@rosolko/til-4-enums-on-steroid-da9a93d294d1">TIL 4: Enums on steroid</a></p><h3>Next story:</h3><p><a href="https://medium.com/@rosolko/publish-java-library-to-jfrog-bintray-and-sonatype-with-gradle-1a3ebd5b8be8">Publish java library to JFrog Bintray and Sonatype with Gradle</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b054072785c5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TIL 4: Enums on steroid]]></title>
            <link>https://medium.com/@rosolko/til-4-enums-on-steroid-da9a93d294d1?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/da9a93d294d1</guid>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[enum]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[dependency-injection]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 25 May 2018 14:12:12 GMT</pubDate>
            <atom:updated>2018-07-06T13:03:18.677Z</atom:updated>
            <content:encoded><![CDATA[<p>What if you can dramatically improve you enum with functions?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-B0aXjZUrRFojvYV." /><figcaption>Photo by <a href="https://unsplash.com/@nate_dumlao?utm_source=medium&amp;utm_medium=referral">Nathan Dumlao</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Image that you write code for “Good People Coffee” company and you need to somehow describe all drinks provided by you company.</p><p>This case you just write down simple enum class with drinks list:</p><pre>public class GoodPeopleCoffeeCompany {<br>  public enum Drink {<br>    <em>ESPRESSO</em>,<br>    <em>TOAST</em>,<br>    <em>TEA</em><br>  }<br>}</pre><p>Good. Next you set price for items:</p><pre>public class GoodPeopleCoffeeCompany {<br>  public enum Drink {<br>    <em>ESPRESSO</em>(1),<br>    <em>TOAST</em>(2),<br>    <em>TEA</em>(3);<br><br>    private final double price;<br><br>    Drink(final double <em>price</em>) {<br>      this.price = <em>price</em>;<br>    }<br><br>    public double getPrice() {<br>      return price;<br>    }<br>  }<br>}</pre><p>Looks good. This case you can easily access drink price and do what ever you want. But image, your businesses grow fast and some day you offer 40% discount for users. Let’s do it:</p><pre>public static void main(String[] <em>args</em>) {<br>  final double <em>discount </em>= 0.6;<br>  final Drink <em>drink </em>= Drink.<em>valueOf</em>(<em>args</em>[0]);<br>  final double <em>finalPrice </em>= <em>drink</em>.getPrice() * <em>discount</em>;<br>}</pre><p>Look good too. But… what if only tea will got discount with 40%? You code get additional verification statement for tea:</p><pre>public static void main(String[] <em>args</em>) {<br>  final double <em>teaDiscount </em>= 0.6;<br>  final Drink <em>drink </em>= Drink.<em>valueOf</em>(<em>args</em>[0]);<br>  final double <em>initialPrice </em>= <em>drink</em>.getPrice();<br>  final double <em>finalPrice </em>= <em>drink</em>.equals(Drink.<em>TEA</em>) <br>    ? <em>initialPrice </em>* <em>teaDiscount<br>    </em>: <em>initialPrice</em>;<br>}</pre><p>Hm… let it be. What if next week espresso also will be on discount with, but only for 20%? Change you enum a little.</p><pre>enum Drink {<br>  <em>ESPRESSO</em>(1, 0.8),<br>  <em>TOAST</em>(2, 1),<br>  <em>TEA</em>(3, 0.6);<br><br>  private final double price;<br>  private final double discount;<br><br>  Drink(final double <em>price</em>, final double <em>discount</em>) {<br>    this.price = <em>price</em>;<br>    this.discount = <em>discount</em>;<br>  }<br><br>  public double getPrice() {<br>    return price;<br>  }<br><br>  public double getDiscount() {<br>    return discount;<br>  }<br>}</pre><p>And get discounted price:</p><pre>public static void main(String[] <em>args</em>) {<br>  final Drink <em>drink </em>= Drink.<em>valueOf</em>(<em>args</em>[0]);<br>  final double <em>initialPrice </em>= <em>drink</em>.getPrice();<br>  final double <em>finalPrice </em>= <em>drink</em>.getPrice() * <em>drink</em>.getDiscount();<br>}</pre><p>Well. “I thinks enough — looks good”. But!!1 Hell no, what you will do if database?! It’s easy, just tune you enum again:</p><pre>enum Drink {<br>  <em>ESPRESSO</em>(DataBase.<em>getEspressoPrice</em>(), <br>   DataBase.<em>getEspressoDiscout</em>()),<br>  <em>TOAST</em>(DataBase.<em>getToastPrice</em>(), DataBase.<em>getToastDiscout</em>()),<br>  <em>TEA</em>(DataBase.<em>getTeaPrice</em>(), DataBase.<em>getTeaDiscout</em>());<br><br>  private final double price;<br>  private final double discount;<br><br>  Drink(final double <em>price</em>, final double <em>discount</em>) {<br>    this.price = <em>price</em>;<br>    this.discount = <em>discount</em>;<br>  }<br><br>  public double getPrice() {<br>    return price;<br>  }<br><br>  public double getDiscount() {<br>    return discount;<br>  }<br>}</pre><p>Cool. But, what if dev, test, uat, prod environments? What if <strong>DEPENDENCY INJECTION</strong>??1</p><blockquote>Houston, we got a problem!</blockquote><p>Can’t properly inject to static class. Lets refactor this weird enum!</p><p>In this case — I will create service that calculate final price for me. Let’s call it PriceService . As long as service class isn’t static — I can easily inject database class.</p><p>Create price service:</p><pre>class PriceService {<br>  @Inject<br>  private DataBase dataBase;<br><br>  double computeEspressoPrice(final double <em>discount</em>) {<br>    return dataBase.getEspressoPrice() * <em>discount</em>;<br>  }<br><br>  double computeToastPrice(final double <em>discount</em>) {<br>    return dataBase.getToastPrice() * <em>discount</em>;<br>  }<br><br>  double computeTeaPrice(final double <em>discount</em>) {<br>    return dataBase.getTeaPrice() * <em>discount</em>;<br>  }<br><br>  double computePrice(final Drink <em>drink</em>, double <em>discount</em>) {<br>    return <em>drink</em>.getPrice().apply(this, <em>discount</em>);<br>  }<br>}</pre><p>Add bi-function to enum:</p><pre>enum Drink {<br>  <em>ESPRESSO</em>(PriceService::computeEspressoPrice),<br>  <em>TOAST</em>(PriceService::computeToastPrice),<br>  <em>TEA</em>(PriceService::computeTeaPrice);<br><br>  public final BiFunction&lt;PriceService, Double, Double&gt; price;<br><br>  Drink(BiFunction&lt;PriceService, Double, Double&gt; <em>price</em>) {<br>   this.price = <em>price</em>;<br>  }<br><br>  public BiFunction&lt;PriceService, Double, Double&gt; getPrice() {<br>    return price;<br>  }<br>}</pre><p>Use it:</p><pre>class MainClass {<br>  @Inject<br>  private PriceService priceService;<br><br>  public void main(String[] <em>args</em>) {<br>    final Drink <em>drink </em>= Drink.<em>valueOf</em>(<em>args</em>[0]);<br>    final double <em>finalPrice </em>= priceService.computePrice(<em>drink</em>, 0.6);<br>  }<br>}</pre><p>Welp! But, again — I must manually pass discount :( Don’t worry — just use function instead of bi-function. Reduce function parameter and get discount from database.</p><p>New enum:</p><pre>enum Drink {<br>  <em>ESPRESSO</em>(PriceService::computeEspressoPrice),<br>  <em>TOAST</em>(PriceService::computeToastPrice),<br>  <em>TEA</em>(PriceService::computeTeaPrice);<br><br>  public final Function&lt;PriceService, Double&gt; price;<br><br>  Drink(Function&lt;PriceService, Double&gt; <em>price</em>) {<br>    this.price = <em>price</em>;<br>  }<br><br>  public Function&lt;PriceService, Double&gt; getPrice() {<br>    return price;<br>  }<br>}</pre><p>New price service:</p><pre>class PriceService {<br>  @Inject<br>  private DataBase dataBase;<br><br>  double computeEspressoPrice() {<br>    return dataBase.getEspressoPrice() * <br>      dataBase.getEspressoDiscout();<br>  }<br><br>  double computeToastPrice() {<br>    return dataBase.getToastPrice() * <br>      dataBase.getToastDiscout();<br>  }<br><br>  double computeTeaPrice() {<br>    return dataBase.getTeaPrice() * <br>      dataBase.getTeaDiscout();<br>  }<br><br>  double computePrice(final Drink <em>drink</em>) {<br>    return <em>drink</em>.getPrice().apply(this);<br>  }<br>}</pre><p>Use it:</p><pre>class MainClass {<br>  @Inject<br>  private PriceService priceService;<br><br>  public void main(String[] <em>args</em>) {<br>    final Drink <em>drink </em>= Drink.<em>valueOf</em>(<em>args</em>[0]);<br>    final double <em>finalPrice </em>= priceService.computePrice(<em>drink</em>);<br>  }<br>}</pre><p>Finally, you have flexible implementation that allow you to use <strong>any</strong> data base <strong>provider</strong> and <strong>any</strong> price <strong>service</strong>, so easy to <strong>switch</strong>, easy to <strong>test</strong>, easy to <strong>mock.</strong></p><p>Easy<strong> </strong>to<strong> customize </strong>price calculation for each enum value type. Like:</p><pre>class EnchancedPriceService {<br>  @Inject<br>  private DataBase dataBase;<br><br>  double computeEspressoPrice() {<br>    return dataBase.getEspressoPrice()<br>     * dataBase.getToastDiscout()<br>     * 0.1;<br>  }<br><br>  double computeToastPrice() {<br>    return dataBase.getToastPrice() <br>      * dataBase.getToastDiscout()   <br>      - 10;<br>  }<br><br>  double computeTeaPrice() {<br>    return dataBase.getTeaPrice() <br>      * dataBase.getTeaDiscout() <br>      / 2;<br>  }<br><br>  double computePrice(final Drink <em>drink</em>) {<br>    return <em>drink</em>.getPrice().apply(this);<br>  }<br>}</pre><p>Even, if you add some new value to enum this construction require you to add method implementation for price service:</p><p>New enum value:</p><pre>enum Drink {<br>  MILK(PriceService::computeMilkPrice);<br>}</pre><p>Create calculate method in price service:</p><pre>class PriceService {<br>  @Inject<br>  private DataBase dataBase;<br><br>  double computeMilkPrice() {<br>    return dataBase.getMilkPrice() * dataBase.getMilkDiscout();<br>  }<br>}</pre><p>Perfect!</p><p><strong>Happy codding</strong>, made you test a little functional! D_D</p><h3>Previous story:</h3><p><a href="https://medium.com/@rosolko/til-3-dependabot-greenmail-and-selenide-junit5-extensions-e2bfbfcb0ef7">TIL 3: Dependabot, GreenMail and Selenide JUnit5 extensions</a></p><h3>Next story:</h3><p><a href="https://medium.com/@rosolko/til-5-made-your-retrofit-rest-client-a-little-reactive-b054072785c5">TIL 5: Made your retrofit rest client reactive a little bit.</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=da9a93d294d1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TIL 3: Dependabot, GreenMail and Selenide JUnit5 extensions]]></title>
            <link>https://medium.com/@rosolko/til-3-dependabot-greenmail-and-selenide-junit5-extensions-e2bfbfcb0ef7?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/e2bfbfcb0ef7</guid>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[junit]]></category>
            <category><![CDATA[selenide]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 18 May 2018 08:40:06 GMT</pubDate>
            <atom:updated>2018-05-25T14:12:51.015Z</atom:updated>
            <content:encoded><![CDATA[<ol><li>GitHub <a href="https://app.dependabot.com/">Dependabot</a> keeps up-to-date your project dependencies.</li><li>JUnit5 extensions: <a href="https://github.com/rosolko/greenmail-junit5">GreenMail</a> and <a href="https://github.com/rosolko/selenide-junit5">Selenide</a></li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*alfcBDzWb_SMkKEq." /><figcaption>“Cappuccino in a brown mug on a brown saucer with heart foam art on a wooden table” by <a href="https://unsplash.com/@jonasjacobsson?utm_source=medium&amp;utm_medium=referral">Jonas Jacobsson</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://app.dependabot.com">Dependabot</a> — quite simple but extremely useful GitHub extension that allow you to keep your project dependencies up-to-date.</p><p>Just app application to you GitHub account and give permission for target repository and set-up pooling interval.</p><p>And when some library will update — bot automatically create new pull request with updates.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2fu3_XY3rCGMqdIWNhDbJw.png" /><figcaption>Just an pull request example</figcaption></figure><p>You can also use a lot of commands for manage behavior — here is a list of:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EoFUKXYsKPzA_bCXJeyzjg.png" /><figcaption>Bot command list</figcaption></figure><p>Next this — it’s JUnit4 rule to JUnit5 extension migration. As example I’ll show you Selenide soft asserts rule.</p><p>By default Selenide can assert softly only for TestNG or Junit4, but since JUnit5 drop rule support — it’s impossible to use old rule for that.</p><p>So, what we have — Junit4 rule:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/11eafaf5cc28fd25d35b951d88bff5de/href">https://medium.com/media/11eafaf5cc28fd25d35b951d88bff5de/href</a></iframe><p>As we can see in this rule actions applying on before and after levels, so let’s find out what we can use from JUnit5.</p><p>We also want to apply actions for each test, so let’s implement BeforeEachCallback and AfterEachCallback extension endpoints and create an extension:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/146b1c43ac8b30d01e587f2021592439/href">https://medium.com/media/146b1c43ac8b30d01e587f2021592439/href</a></iframe><p>As you can see — I’ve made some changes across original rule:</p><ol><li>Move errorCollector initialization to constructor — it’s linked with extension using, there are 2 way to use extension:</li></ol><p>The first one:</p><pre><a href="http://twitter.com/ExtendWith">@ExtendWith</a>(SoftAssertsExtension.class)</pre><p>In this case all going flawless without any problems.</p><p>And the second one:</p><pre>@RegisterExtension <br>SoftAssertsExtension assertsExtension = new SoftAssertsExtension();</pre><p>And for this case I’ve move initialization to constructor. I’m using it for tests, that’s also have some interesting points. Also in future — it’s will allow to add parameterized constructor and property use it.</p><p>2. Add gerErrorCollector method to test asserts — it needed to check that all errors will collect and throw for current test:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0ccf8f8db693134c514139714967c23f/href">https://medium.com/media/0ccf8f8db693134c514139714967c23f/href</a></iframe><p>And check test console output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7eAfwdQLBB_QzMKhM110fQ.png" /><figcaption>Catch all fails</figcaption></figure><p>Perfect!</p><p>I’m also add JUnit5 extension for GreenMail and it’s also copy logic from original Junit4 rule.</p><p>You take get and try both extensions by links:</p><ul><li><a href="https://github.com/rosolko/greenmail-junit5">rosolko/greenmail-junit5</a></li><li><a href="https://github.com/rosolko/selenide-junit5">rosolko/selenide-junit5</a></li></ul><h3>Previous story:</h3><p><a href="https://medium.com/@rosolko/this-week-i-learned-1-fefebc77f17a">TIL 2: Win against Appium, new Selenium version, Gradle BOM and more…</a></p><h3>Next story:</h3><p><a href="https://medium.com/@rosolko/til-4-enums-on-steroid-da9a93d294d1">TIL 4: Enums on steroid</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e2bfbfcb0ef7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TIL 2: Win against Appium, new Selenium version, Gradle BOM and more…]]></title>
            <link>https://medium.com/@rosolko/this-week-i-learned-1-fefebc77f17a?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/fefebc77f17a</guid>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[allure]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[appium]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 11 May 2018 13:22:47 GMT</pubDate>
            <atom:updated>2018-05-18T08:40:45.935Z</atom:updated>
            <content:encoded><![CDATA[<ol><li>Small win against Appium.</li><li>New version of selenium.</li><li>Allure configuration for Gradle.</li><li>Gradle BOM imports.</li><li>Authorization via browser local storage.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*q2IdT9QsnYP-VFZ9." /><figcaption>Photo by <a href="https://unsplash.com/@rosiekerr?utm_source=medium&amp;utm_medium=referral">Rosie Kerr</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Last <a href="https://medium.com/@rosolko/this-week-i-learned-0-2da20e148c6d">week</a> I upgrade Appium to 1.8.0 version and face “Keyboard is not present” issue and now looks like I found solution.</p><p>You just need to execute 2 scripts before test run, for example as a pre step on your CI:<br> 1. defaults write com.apple.iphonesimulator ConnectHardwareKeyboard 1</p><p>2. defaults write com.apple.iphonesimulator ToggleSoftwareKeyboard 1</p><p>As I understood iOS 11.3 and latest Appium required keyboard to be displayed all the time. So if keyboard hide some element you should tune your test to hide keyboard or swipe a little until element visible.</p><p>I’m consider to swipe for now. Code that I’m using:</p><pre>public enum Direction {<br>    <em>UP,</em><br>    <em>DOWN,</em><br>    <em>RIGHT,</em><br>    <em>LEFT<br>}</em></pre><pre>private void swipe(final Direction <em>direction</em>) {<br>    final Map&lt;String, Object&gt; <em>params </em>= new HashMap&lt;&gt;();<br>    <em>params</em>.put(&quot;direction&quot;, <em>direction</em>.name().toLowerCase());<br>    <em>getIosDriver</em>().executeScript(&quot;mobile: swipe&quot;, <em>params</em>);<br>}</pre><p>As you know or not — selenium <a href="https://github.com/SeleniumHQ/selenium/blob/master/java/CHANGELOG">released</a> 3.12.0 version. So I try to update it for my project and got strange error during mobile driver initialization:</p><pre>java.lang.NoSuchMethodError: org.openqa.selenium.json.JsonOutput.write(Ljava/lang/Object;Ljava/lang/reflect/Type;)Lorg/openqa/selenium/json/JsonOutput;<br>at org.openqa.selenium.remote.NewSessionPayload.writeTo(NewSessionPayload.java:247)</pre><pre>...</pre><pre>Suppressed: java.io.IOException: Incomplete document<br> at com.google.gson.stream.JsonWriter.close(JsonWriter.java:559)<br> at org.openqa.selenium.json.JsonOutput.close(JsonOutput.java:38)<br> at org.openqa.selenium.remote.NewSessionPayload.writeTo(NewSessionPayload.java:270)</pre><p>Okay. Let’s wait for new Appium release.</p><p>This week I’m also create a short guide how to configure Allure 2 for Gradle with some extra tools. <a href="https://medium.com/@rosolko/simple-allure-2-configuration-for-gradle-8cd3810658dd">Here it is</a>.</p><p>Finally I’m implement BOM imports for my project that’s was introduces in Gradle version 4.6. It’s simple.</p><p>For fist — add enable this feature preview for Gradle. Let’s it in settings.gradle:</p><pre>enableFeaturePreview(&#39;IMPROVED_POM_SUPPORT&#39;)</pre><p>Next — improve your build script. <strong>Note</strong>: not all libraries already support this features, but JUnit5 and Log4j2 does:</p><p>Old dependencies section:</p><pre>ext {<br>    log4jVersion = &#39;2.11.0&#39;<br>    junitJupiterVersion = &#39;5.2.0&#39;<br>}</pre><pre>...</pre><pre>dependencies {<br>    api(&quot;org.apache.logging.log4j:log4j-api:$log4jVersion&quot;)<br> <br>    testImplementation(&quot;org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion&quot;)<br>    testImplementation(&quot;org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion&quot;)</pre><pre>testRuntimeOnly(&quot;org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-core:$log4jVersion&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-jul:$log4jVersion&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-jcl:$log4jVersion&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion&quot;)<br>}</pre><p>Becomes to new one:</p><pre>dependencies {<br>    api(&quot;org.apache.logging.log4j:log4j-bom:2.11.0&quot;)<br>    api(&quot;org.apache.logging.log4j:log4j-api&quot;)</pre><pre>    testImplementation(&quot;org.junit:junit-bom:5.2.0&quot;)<br>    testImplementation(&quot;org.junit.jupiter:junit-jupiter-api&quot;)<br>    testImplementation(&quot;org.junit.jupiter:junit-jupiter-params&quot;)</pre><pre>    testRuntimeOnly(&quot;org.junit.jupiter:junit-jupiter-engine&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-core&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-jul&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-jcl&quot;)<br>    testRuntimeOnly(&quot;org.apache.logging.log4j:log4j-slf4j-impl&quot;)<br>}</pre><p>From my point of view configuration become more easier.</p><p>Also I <a href="https://medium.com/@rosolko/fast-authorization-level-local-storage-6c84e9b3cef1">found</a> that session can be also signed using browser local storage instead of cookie.</p><h3>Previous story:</h3><p><a href="https://medium.com/@rosolko/this-week-i-learned-0-2da20e148c6d">TIL 1: New versions of Appium and Windows, Gradle fail fast feature</a></p><h3>Next story:</h3><p><a href="https://medium.com/@rosolko/til-3-dependabot-greenmail-and-selenide-junit5-extensions-e2bfbfcb0ef7">TIL 3: Dependabot, GreenMail and Selenide JUnit5 extensions</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fefebc77f17a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fast authorization. Level— local storage.]]></title>
            <link>https://medium.com/@rosolko/fast-authorization-level-local-storage-6c84e9b3cef1?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/6c84e9b3cef1</guid>
            <category><![CDATA[automation-testing]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 11 May 2018 13:19:23 GMT</pubDate>
            <atom:updated>2018-05-11T13:19:23.285Z</atom:updated>
            <content:encoded><![CDATA[<p>One more method to authorize using faster way.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*enK04nv9Mh-sU3MA." /><figcaption>Photo by <a href="https://unsplash.com/@stairhopper?utm_source=medium&amp;utm_medium=referral">Alex Holyoake</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://medium.com/@rosolko/boost-you-autotests-with-fast-authorization-b3eee52ecc19">Previously</a> I talk about fast authorization via cookie. But now, I found one more variant — it’s a browser local storage.</p><p>Mechanism look like cookie, but we need to change our JavaScript code a little.</p><p>Last time I use duplicated code and it’s look kinda weird. Let’s upgrade it!</p><p>Leave old JavaScript executor and open methods untouched:</p><pre>private void executeJavaScript(final String <em>jsCode</em>) {<br>    ((JavascriptExecutor) webDriver).executeScript(<em>jsCode</em>);<br>}</pre><pre>private void open(final String url) {<br>    webDriver.get(url);<br>}</pre><p>Create private method that set name value in local storage:</p><pre>private void setItemInLocalStorage(final String <em>item</em>, final String <em>value</em>) {<br>  final String <em>jsCode </em>= String.<em>format</em>(<br>    &quot;window.localStorage.setItem(&#39;%s&#39;,&#39;%s&#39;);&quot;, <br>    <em>item</em>, <br>    <em>value</em>);</pre><pre>  <em>executeJavaScript</em>(<em>jsCode</em>);<br>}</pre><p>Create a new public method that will authorize application:</p><pre>public void authorizeWithLocalStorage(final AccessToken <em>accessToken</em>) {<br>  <em>open</em>(&quot;https://my-supa-dupa-app.com/robots.txt&quot;);</pre><pre>  setItemInLocalStorage(&quot;$accessTokenId&quot;, <em>accessToken</em>.getId());<br>  setItemInLocalStorage(&quot;$currentUserId&quot;, <em>accessToken</em>.getUserId());<br>}</pre><p>Works as expected stable and fast. Awesome!</p><p>Happy testing! ~_~</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6c84e9b3cef1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Simple Allure 2 configuration for Gradle]]></title>
            <link>https://medium.com/@rosolko/simple-allure-2-configuration-for-gradle-8cd3810658dd?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/8cd3810658dd</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[selenide]]></category>
            <category><![CDATA[junit]]></category>
            <category><![CDATA[allure]]></category>
            <category><![CDATA[gradle]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 11 May 2018 07:55:56 GMT</pubDate>
            <atom:updated>2021-08-13T07:41:08.966Z</atom:updated>
            <content:encoded><![CDATA[<p>From many time past it’s easy to configure allure with Gradle</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*hqoQ2ex5rs-UYu2e." /><figcaption>“Green plants covering the walls of a multi-story building” by <a href="https://unsplash.com/@danist07?utm_source=medium&amp;utm_medium=referral">贝莉儿 NG</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>For first you need to apply <a href="https://plugins.gradle.org/plugin/io.qameta.allure">allure gradle plugin</a>. I’m using latest gradle version so let’s use new plugin applying mechanism.</p><pre>plugins {<br>    id &#39;io.qameta.allure&#39; version &#39;2.8.1&#39;<br>}</pre><p>Next step will be about configuration. In my test I’m using Junit5 and I want to use latest allure version so I’ll manually pass latest version in config and also I have to use custom download link format to get a latest version of allure commandline.</p><pre>allure {<br>    version = &#39;2.14.0&#39;<br>}</pre><p>Last but not least — tools <a href="https://github.com/allure-framework/allure-java#allure-java-integrations-">integration</a>. There are many of integration, I’m showing my set:</p><ol><li><a href="https://github.com/allure-framework/allure-java#junit-5">Junit5</a></li><li><a href="https://github.com/allure-framework/allure-java#selenide">Selenide</a></li><li><a href="https://github.com/allure-framework/allure-java#okhttp">Retforit/Okhttp</a></li></ol><p>I’m going to apply this integration as dependencies.</p><p>Retforit/Okhttp. I’m using implementation scope because it’s required some code changes to work property:</p><pre>dependencies {<br>    implementation(&#39;io.qameta.allure:allure-okhttp3:2.14.0&#39;)<br>}</pre><p>Code changes — it’s just additional Okhttp client interceptor:</p><pre>final AllureOkHttp3 <em>allureOkHttp3 </em>= new AllureOkHttp3();</pre><pre>final OkHttpClient okHttpClient = new OkHttpClient.Builder()<br>    .addInterceptor(<em>allureOkHttp3</em>)<br>    .build();</pre><p>Junit5 integration isn’t required any code changes, just place it in right scope, use testRuntimeOnly scope for that:</p><pre>dependencies {<br>    testRuntimeOnly(&#39;io.qameta.allure:allure-junit5:2.14.0&#39;)<br>}</pre><p>That’s all.</p><p>Selenide integration also required some code changes, but lets apply testImplementation scope because it’s required only SelenideLogger listener registration that can be done before tests:</p><pre>dependencies {<br>    testImplementation(&#39;io.qameta.allure:allure-selenide:2.14.0&#39;)<br>}</pre><p>Code changes — add listener on BeforeAll step:</p><pre>@BeforeAll<br>public void setUp() {<br>    SelenideLogger.<em>addListener</em>(&quot;allure&quot;, new AllureSelenide());<br>}</pre><p>And don’t forget to remove listener after tests. Let’s do it on AfterAll step:</p><pre>@AfterAll<br>public void tearDown() {<br>    SelenideLogger.<em>removeListener</em>(&quot;allure&quot;);<br>}</pre><p>I’m also advise to create allure property file and point results directory to avoid miss target results creation. I’m place it inside src/test/resources folder and call it allure.properties and add content:</p><pre>allure.results.directory=build/allure-results</pre><p>Great! Now you’re all done.</p><p>From now you just need to execute your test and after tests run gradle task allureReport that creates Allure report or allureServethat creates Allure report and opens it in the default browser.</p><p>Happy testing! D_D</p><p>You can find complete project on GitHub.</p><p><a href="https://github.com/rosolko/allure-gradle-configuration">rosolko/allure-gradle-configuration</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8cd3810658dd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TIL 1: New versions of Appium and Windows, Gradle fail fast feature]]></title>
            <link>https://medium.com/@rosolko/this-week-i-learned-0-2da20e148c6d?source=rss-dbc509da9dfd------2</link>
            <guid isPermaLink="false">https://medium.com/p/2da20e148c6d</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[windows-10]]></category>
            <dc:creator><![CDATA[Aliaksandr Rasolka]]></dc:creator>
            <pubDate>Fri, 04 May 2018 13:09:51 GMT</pubDate>
            <atom:updated>2018-05-16T13:56:03.156Z</atom:updated>
            <content:encoded><![CDATA[<ol><li>New version <a href="https://github.com/appium/appium/releases/tag/v1.8.0">1.8.0</a> of <a href="https://github.com/appium/appium">Appium</a></li><li>Windows 10 April 2018 <a href="https://blogs.windows.com/windowsexperience/2018/04/30/how-to-get-the-windows-10-april-2018-update">Update</a></li><li><a href="https://docs.gradle.org/4.6/userguide/java_plugin.html#sec:test_execution">Gradle</a> test execution features.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*N9SRH5K5E-N_69Yx." /><figcaption>Photo by <a href="https://unsplash.com/@benkolde?utm_source=medium&amp;utm_medium=referral">Ben Kolde</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Gradle introduce failFast feature in 4.6 version and it’s super useful if you don’t want to wait a lot for tests complete and fail build as fast as possible if something going wrong.</p><p>On my project I’m applying this option to test and check for <strong>Pull </strong>and <strong>Merge Requests </strong>during CI builds where I don’t care about all failures and where even one failed test fail request.</p><p>Just bypass extra parameter: ./gradlew check test --fail-fast</p><p>Or you can use it on regular base by tuning test task:</p><pre>test {<br>    failFast = true<br>}</pre><p>14 days after release I’m decide to update automation stack with new Xcode to 9.3 version and transitive iOS Simulator to 11.3 verison.</p><blockquote>As claimed in release notes 1.8.0 version bring Support for iOS 11.3/Xcode 9.3</blockquote><p>So, update all things, run tests… and catch fast exception:</p><pre>org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Error Domain=com.facebook.WebDriverAgent Code=1 “Keyboard is not present” UserInfo={NSLocalizedDescription=Keyboard is not present}</pre><p>Research a lot… and research… and research… There are no solution for now :(</p><p>Solution from issues won’t work.</p><p>Issue references:</p><ol><li><a href="https://github.com/appium/appium/issues/8412">Appium github #8412</a></li><li><a href="https://github.com/facebook/WebDriverAgent/issues/574">Github Facebook WebDriverAgent #574</a></li></ol><p>So, decide to downgrade Appium to 1.7.2 version — and it’s work! Even with latest version of iOS/Xcode.</p><p>Waiting to next releases.</p><p><strong>Update</strong>: Work well now… <em>*magic*</em></p><p>Windows 10 April 2018 update. Last but not least.</p><p>If your microphone won’t work just check enabled option under Settings &gt; Privacy &gt; Microphone &gt; Allow apps to access your microphone to save you time and nerves.</p><p>I don’t know why, but this option was disabled after update :C</p><h3>Next story</h3><p><a href="https://medium.com/@rosolko/this-week-i-learned-1-fefebc77f17a">TIL 2: Win against Appium, new Selenium version, Gradle BOM and more…</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2da20e148c6d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>