<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ivan Shafran</title>
    <description>The latest articles on Forem by Ivan Shafran (@ivanshafran).</description>
    <link>https://forem.com/ivanshafran</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%2F1012976%2F0320ed55-32df-4386-906d-de9501051d39.jpeg</url>
      <title>Forem: Ivan Shafran</title>
      <link>https://forem.com/ivanshafran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ivanshafran"/>
    <language>en</language>
    <item>
      <title>Evolution of unit tests in Android</title>
      <dc:creator>Ivan Shafran</dc:creator>
      <pubDate>Sun, 22 Oct 2023 21:19:47 +0000</pubDate>
      <link>https://forem.com/ivanshafran/evolution-of-unit-tests-in-android-32c2</link>
      <guid>https://forem.com/ivanshafran/evolution-of-unit-tests-in-android-32c2</guid>
      <description>&lt;p&gt;In the article we take a look at the unit test evolution from beginner to pro-level. “Rate us” dialog is a popular feature, and it will be a good example. Typical “rate us” dialog has requirements like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should be shown after some condition or user action&lt;/li&gt;
&lt;li&gt;Has “rate us” button that leads to Google Play&lt;/li&gt;
&lt;li&gt;Has “remind me later” button that schedules dialog to show after some time(2 months in our case)&lt;/li&gt;
&lt;li&gt;Has “never show again” button that hides the dialog forever&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fejyxu1oeoslyezenakok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fejyxu1oeoslyezenakok.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Zero version: no unit tests
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fl6dtiw4z982odyqn3jls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fl6dtiw4z982odyqn3jls.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the beginning of the Android community, unit tests were not so popular. There are several arguments behind this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests require time to be implemented and maintained&lt;/li&gt;
&lt;li&gt;Apps were mostly simple&lt;/li&gt;
&lt;li&gt;The framework itself isn’t friendly for writing unit tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  First version: my first unit test
&lt;/h1&gt;

&lt;p&gt;But unfortunately, “rate us” dialog is a too vital feature to ignore unit tests. The more users leave reviews, the more new users your app gets. Also, it should not be annoying. Otherwise, people will leave 1-star reviews.&lt;/p&gt;

&lt;p&gt;I’ve implemented a sample app that follows the logic described in the introduction. To trigger dialog, a user should click button two times. &lt;a href="https://github.com/IvanShafran/android-unit-tests-evolution-rate-us-dialog" rel="noopener noreferrer"&gt;Check code in the repository.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To code first unit test, I had to do several things.&lt;/p&gt;

&lt;h1&gt;
  
  
  Learn a little about JUnit Framework
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Feel free to skip this chapter if you are familiar with JUnit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JUnit is the most popular testing framework for Java. It’s included in dependencies by default to all new Android projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    testImplementation 'junit:junit:4.12'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To write a test, you should create a class in test folder which is created by default and contains ExampleUnitTest.java. Usually, if devs test SomeClass , devs will name class with tests SomeClassTest . Moreover, most times, it belongs to the same Java package.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcjfj2ao4l40oe5p7kujf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcjfj2ao4l40oe5p7kujf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see a simple test. You should annotate all test methods with org.junit.Test . Android Studio will automatically show the run test button. assertEquals will throw an exception if arguments are not equal. JUnit marks test as passed if it ends without any exception.&lt;/p&gt;

&lt;h1&gt;
  
  
  Abstract an Android from business logic
&lt;/h1&gt;

&lt;p&gt;In Android, you can’t write unit tests for a class that uses the Android framework. But wait… WHAT???!&lt;/p&gt;

&lt;p&gt;Yes, the framework requires a specific environment and you can’t run it on any JVM. In unit tests, all Android classes are mocked to throw an exception. The best that we can do is to force it to return default values instead of throwing an exception.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// In application module build.gradle
android.testOptions {
    unitTests.returnDefaultValues = true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to the dialog, we certainly use Android classes like Activity/Fragment, View, Dialog, and others for “rate us” feature. Therefore we can’t write unit tests without a bit of effort.&lt;/p&gt;

&lt;p&gt;First, I created an interface for every Android dependency which I use for “rate us” showing logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferences&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getBuyCount&lt;/span&gt;&lt;span class="o"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;BuyPreferencesImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;context:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferences&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="nl"&gt;sharedPreferences:&lt;/span&gt; &lt;span class="nc"&gt;SharedPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSharedPreferences&lt;/span&gt;&lt;span class="o"&gt;(...)&lt;/span&gt;

    &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;putInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BUY_COUNT_KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BUY_COUNT_KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, I created an interface for every Java dependency that can’t be used directly in tests. More specifically, it is System.currentTimeMillis() .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Time {
    fun getCurrentTimeMillis(): Long
}

class TimeImpl : Time {
    override fun getCurrentTimeMillis() = System.currentTimeMillis()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last, I applied the dependency inversion principle toShowRateUsLogic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ShowRateUsLogic(
    private val rateUsPreferences: RateUsPreferences,
    private val buyPreferences: BuyPreferences,
    private val time: Time
) {
    fun shouldShowRateUs(): Boolean {
        val timeFromLastShown = time.getCurrentTimeMillis() - rateUsPreferences.getLastShownTimeMillis()
        return when {
            // User doesn't want to see "rate us" again
            rateUsPreferences.isNeverShownAgainClicked() -&amp;gt; false
            // User already rated the app
            rateUsPreferences.isRateNowClicked() -&amp;gt; false
            // "Rate us" should be shown after 2 "buy" clicked
            buyPreferences.getBuyCount() &amp;lt; 2 -&amp;gt; false
            // Show "rate us" only first time or if passed two months since last shown time
            timeFromLastShown &amp;lt; TimeUnit.DAYS.toMillis(60) -&amp;gt; false
            else -&amp;gt; true
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Mocks
&lt;/h1&gt;

&lt;p&gt;Now we should write mock classes for unit tests. I’ll show one mock below. You can check all the mocks &lt;a href="https://github.com/IvanShafran/android-unit-tests-evolution-rate-us-dialog/tree/master/app/src/test/java/com/github/ivanshafran/unit_tests_evolution/v1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferencesMock&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferences&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getBuyCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  First unit test
&lt;/h1&gt;

&lt;p&gt;After hardworking, it is a pleasure to code the first unit test :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ShowRateUsLogicTest {
    private RateUsPreferencesMock rateUsPreferences;
    private BuyPreferencesMock buyPreferences;
    private TimeMock time;
    private ShowRateUsLogic showRateUsLogic;

    @Test public void test1() {
        rateUsPreferences = new RateUsPreferencesMock();
        buyPreferences = new BuyPreferencesMock();
        time = new TimeMock();
        showRateUsLogic = new ShowRateUsLogic(rateUsPreferences, buyPreferences, time);

        buyPreferences.incrementBuyCount();
        time.setCurrentTimeMillis(new Date(2019, 6, 7).getTime());

        Assert.assertFalse(showRateUsLogic.shouldShowRateUs());
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.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%2Foobec0ox610c7ssoi5sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Foobec0ox610c7ssoi5sb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To be honest, it is not. But I wrote it in the “my first unit test” style. And I’ll fix it in the next chapters.&lt;/p&gt;

&lt;h1&gt;
  
  
  Second version: code cleaning
&lt;/h1&gt;

&lt;p&gt;The first unit test is cool but if no one can understand it, then it is useless. Therefore I’ve refactored test class:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;setUp is marked with @Before annotation. It makes the method to be invoked before every unit test. We’ll move the common test code to setUp method.&lt;/li&gt;
&lt;li&gt;A good test method has a meaningful name. It’s better for reading and also it appears in test reports. We’ll rename test1 to onFirstCheckAndOneClickItShouldNotShow .&lt;/li&gt;
&lt;li&gt;I’ve added a more complicated test. The test name is too short to express all the information. That’s why we’ll add comments to the method body.&lt;/li&gt;
&lt;li&gt;For the last step, we’ll delete unnecessary and wrong time set.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShowRateUsLogicTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// property declaration is skipped&lt;/span&gt;
    &lt;span class="nd"&gt;@Before&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RateUsPreferencesMock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferencesMock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TimeMock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;showRateUsLogic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShowRateUsLogic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onFirstCheckAndOneClickItShouldNotShow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertFalse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldShowRateUs&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onThreeClicksAndItShouldShow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// clicked three times&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// set first dialog show time&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Calendar&lt;/span&gt; &lt;span class="n"&gt;calendar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;JULY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLastShownTimeMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeInMillis&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// set current time to be 90 days after first show&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCurrentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeInMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DAYS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldShowRateUs&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Third version: mocking libraries
&lt;/h1&gt;

&lt;p&gt;As mentioned, we can not use Android classes in unit tests. But almost all our classes do it. And it’s quite boring to create an interface, an implementation and a mock for every class.&lt;/p&gt;

&lt;p&gt;Fortunately, there is an alternative way. We can use mocking libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mockito
&lt;/h2&gt;

&lt;p&gt;Directly to the most frequently used API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Mockito.mock(Class) to mock any interface or class&lt;/li&gt;
&lt;li&gt;Use Mockito.when(instance.method()).thenReturn(value) to mock method call&lt;/li&gt;
&lt;li&gt;Use Mockito.verify(instance).method() to check if method was called&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/IvanShafran/shared-preferences-mock" rel="noopener noreferrer"&gt;Shared preferences mock&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In my practice, shared preferences class is the most used Android dependency in business logic. Therefore I’ve implemented shared preferences mock library which mimics Android implementation. Now you can use shared preferences in unit tests with one additional line of code ;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShowRateUsLogicTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// property declaration is skipped&lt;/span&gt;
    &lt;span class="nd"&gt;@Before&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;mockedContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SPMockBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RateUsPreferencesImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockedContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;buyPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BuyPreferencesImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockedContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockito&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;showRateUsLogic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShowRateUsLogic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// first test code leaves the same&lt;/span&gt;

    &lt;span class="c1"&gt;// second test code changed only in time mocking&lt;/span&gt;
    &lt;span class="nd"&gt;@Test&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onThreeClicksAndItShouldShow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;

        &lt;span class="c1"&gt;// set current time to be 90 days after first show&lt;/span&gt;
        &lt;span class="nc"&gt;Mockito&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeInMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DAYS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldShowRateUs&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Fourth version: Kotlin
&lt;/h1&gt;

&lt;p&gt;Kotlin is a good language for Android development. And also, it can bring improvements to unit tests code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We’ll rename the test method name using spaces enclosed in backticks&lt;/li&gt;
&lt;li&gt;We’ll move the common preparation code to function with default arguments&lt;/li&gt;
&lt;li&gt;We’ll delete unnecessary comments because we can use named arguments&lt;/li&gt;
&lt;li&gt;We’ll use mockito-kotlin cause it has a more idiomatic and compact syntax
&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;class&lt;/span&gt; &lt;span class="nc"&gt;ShowRateUsLogicTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// property declaration and setup are skipped&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;buyClickedTimes&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;isNeverShownAgainClicked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;isRateNowClicked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;lastShownTimeMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&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="n"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&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="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyClickedTimes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;buyPreferences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incrementBuyCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isNeverShownAgainClicked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setNeverShownAgainClicked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isRateNowClicked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRateNowClickedClicked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;rateUsPreferences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLastShownTimeMillis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastShownTimeMillis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;whenever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&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;onFirstCheckAndOneClickItShouldNotShow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyClickedTimes&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="nc"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&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;onThreeClicksAndItShouldShow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;buyClickedTimes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lastShownTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;currentTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;MORE_THAN_TWO_MONTHS&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&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;h1&gt;
  
  
  Fifth version: Spek
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/spekframework/spek" rel="noopener noreferrer"&gt;Spek &lt;/a&gt;is a unit testing framework for Kotlin which supports Specification and Gherkin style.&lt;/p&gt;

&lt;p&gt;Personally, the crucial features of Spek are:&lt;/p&gt;

&lt;p&gt;Ability to structure test due to condition&lt;br&gt;
Ability to construct tests on the go(cause test code is a lambda, not a method)&lt;br&gt;
I intentionally will not describe syntax because it is easy to understand. And if you are interested in Spek then check out this &lt;a href="https://spekframework.org/core-concepts/" rel="noopener noreferrer"&gt;link&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;ShowRateUsLogicTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Spek&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// property declaration, setup and preparation are skipped&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"show rate us logic"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"first conditions checks"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buy clicked once"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;beforeEachTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyClickedTimes&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="p"&gt;}&lt;/span&gt;

                &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should not show 'rate us'"&lt;/span&gt;&lt;span class="p"&gt;)&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;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&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;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buy clicked two times"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;beforeEachTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyClickedTimes&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="p"&gt;}&lt;/span&gt;

                &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should show 'rate us'"&lt;/span&gt;&lt;span class="p"&gt;)&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&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;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'rate us' was shown already, and user clicked 'show me later' on the dialog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"less than two months passed and user clicks buy"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;beforeEachTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;buyClickedTimes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;lastShownTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;currentTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;LESS_THAN_TWO_MONTHS&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should not show 'rate us' again"&lt;/span&gt;&lt;span class="p"&gt;)&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;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&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;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"more than two months passed and user clicks buy"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;beforeEachTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;prepareConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;buyClickedTimes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;lastShownTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;currentTimeMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SOME_DAY_IN_MILLIS&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;MORE_THAN_TWO_MONTHS&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should show 'rate us' again"&lt;/span&gt;&lt;span class="p"&gt;)&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showRateUsLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldShowRateUs&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;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;Moreover, Spek generates a structured test report in Android Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fhqn3r8pjpftxjj1vb9xy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fhqn3r8pjpftxjj1vb9xy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus part
&lt;/h1&gt;

&lt;p&gt;The article about unit tests in Android will not be full without several mentions. If you have more links to mention, please share them in comments and I’ll add them to the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Robolectric
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Robolectric is a framework that brings fast and reliable unit tests to Android. Tests run inside the JVM on your workstation in seconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is not a pure unit testing but allows us to test Android APIs without launching a device or emulator. On the other hand, it has a bigger test run time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assertion frameworks
&lt;/h2&gt;

&lt;p&gt;‘Rate us’ dialog logic has boolean return value, therefore we used simple assertTrue or assertFalse. But for more complicated tests, there’s not enough flexibility in default assertions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hamcrest is a framework for writing matcher objects allowing ‘match’ rules to be defined declaratively.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertThat(Math.sqrt(-1), is(notANumber()))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;AssertJ — fluent assertions java library.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertThat(frodo.getName()).isEqualTo("Frodo")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Truth makes your test assertions and failure messages more readable. Similar to AssertJ, it natively supports many JDK and Guava types, and it is extensible to others.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertThat(notificationText).contains("testuser@google.com")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>android</category>
      <category>testing</category>
    </item>
    <item>
      <title>Android open source app secure build config</title>
      <dc:creator>Ivan Shafran</dc:creator>
      <pubDate>Sun, 22 Oct 2023 19:37:46 +0000</pubDate>
      <link>https://forem.com/ivanshafran/android-open-source-app-secure-build-config-38gi</link>
      <guid>https://forem.com/ivanshafran/android-open-source-app-secure-build-config-38gi</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR repository&lt;/strong&gt;: &lt;a href="https://github.com/IvanShafran/android-open-source-app-signing" rel="noopener noreferrer"&gt;https://github.com/IvanShafran/android-open-source-app-signing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default Android app has debug build in which APK is signed automatically with debug keystore. But for a release build, you have to sign with a manually created keystore.&lt;/p&gt;

&lt;p&gt;If you develop an open source app, you can’t keep a keystore together with code due to security reasons. You can easily exclude it in the .gitignore file, but in this case, your contributors won’t build an app in a release variant. It complicates open source development because some bugs can only be discovered in a release build. Moreover, you can’t run UI tests for release builds directly from a repository. Let’s solve the problem!&lt;/p&gt;

&lt;h1&gt;
  
  
  First step. Create two keystores
&lt;/h1&gt;

&lt;p&gt;Create one keystore for production build with strong passwords. And create another for contributors with any password. Place them both in the main app module folder. For default created project folder is “app.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/studio/publish/app-signing#generate-key" rel="noopener noreferrer"&gt;How to generate keystore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fc5apwe9su3099os9qp89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fc5apwe9su3099os9qp89.png" alt="Two files: production.jks and contributor.jks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point, add &lt;code&gt;production.jks&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; .&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Second step. Create two property files
&lt;/h1&gt;

&lt;p&gt;Create two properties files in the same folder as described below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Sign key alias for contributors.jks
releaseSignKeyAlias=key
# Sign key password for contributors.jks
releaseSignKeyPassword=password
# Path to contributors.jks
releaseStoreFilePath=./contributors.jks
# Keystore password for contributors.jks
releaseStorePassword=password


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

&lt;/div&gt;

&lt;p&gt;Also, create production.properties with production key and passwords.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7tfc9ocruh2h4p05x32k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7tfc9ocruh2h4p05x32k.png" alt="Two files: contributor.properties and production.properties"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point, add &lt;code&gt;production.properties&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; .&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Third step. Add script
&lt;/h1&gt;

&lt;p&gt;Add script listed below to the same folder and apply it in build.gradle. Script checks if production properties file exists in the folder and if it exists script uses production keystore otherwise contributors. That’s all magic :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t forget to add production.jks and production.properties to .gitignore .&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;settings.gradle:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

def propertiesFilename = "production.properties"
if (!project.file(propertiesFilename).exists()) {
    propertiesFilename = "contributors.properties"
}

def signingProperties = new Properties()
signingProperties.load(new FileInputStream(file(propertiesFilename)))

android {
    signingConfigs {
        release {
            keyAlias signingProperties.releaseSignKeyAlias
            keyPassword signingProperties.releaseSignKeyPassword
            storeFile file(signingProperties.releaseStoreFilePath)
            storePassword signingProperties.releaseStorePassword
        }
    }
}


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

&lt;/div&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
apply from: 'signing.gradle'
...
android {
  ...
  buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3a0b7dg4j1xd86e0udq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3a0b7dg4j1xd86e0udq4.png" alt="Two files highlighted: build.gradle and settings.gradle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full sample here:&lt;/strong&gt; &lt;a href="https://github.com/IvanShafran/android-open-source-app-signing" rel="noopener noreferrer"&gt;https://github.com/IvanShafran/android-open-source-app-signing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Improve Android unit tests with shared preferences mock library</title>
      <dc:creator>Ivan Shafran</dc:creator>
      <pubDate>Mon, 27 Feb 2023 02:17:07 +0000</pubDate>
      <link>https://forem.com/ivanshafran/improve-android-unit-tests-with-shared-preferences-mock-library-2i1l</link>
      <guid>https://forem.com/ivanshafran/improve-android-unit-tests-with-shared-preferences-mock-library-2i1l</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: &lt;a href="https://github.com/IvanShafran/shared-preferences-mock" rel="noopener noreferrer"&gt;Shared preferences mock&lt;/a&gt; is the lightweight library let you increase coverage of unit tests and simplify code for them with one line of code.&lt;/p&gt;

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

&lt;p&gt;Unit test on Android uses a framework mock where every method throws &lt;code&gt;UnsupportedOperationException&lt;/code&gt;or does nothing depending on your settings in Gradle &lt;code&gt;testOptions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In unit tests, we mostly test business logic, which often depends on preferences. Therefore you should mock all classes which use &lt;code&gt;SharedPreferences&lt;/code&gt;inside. It is a lot of boilerplate in tests. Moreover, because of this limitation, we never test preferences class code. However, it also may have bugs.&lt;/p&gt;

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

&lt;p&gt;The library implements &lt;code&gt;SharedPreferences&lt;/code&gt;, &lt;code&gt;Context.getSharedPreferences&lt;/code&gt; and &lt;code&gt;Context.deleteSharedPreferences&lt;/code&gt; in memory using only Java API.&lt;/p&gt;

&lt;p&gt;It allows you to use classes with shared preferences in unit tests as is and also to write tests for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Add it in your root build.gradle at the end of repositories:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

&lt;span class="n"&gt;allprojects&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;mavenCentral&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;Add the dependency to module build.gradle:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'io.github.ivanshafran:shared-preferences-mock:1.2.4'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;In most cases, preference class or preference utils depends on &lt;code&gt;Context&lt;/code&gt;. In unit test instead of real &lt;code&gt;Context&lt;/code&gt; pass &lt;code&gt;Context&lt;/code&gt; created by &lt;code&gt;new SPMockBuilder().createContext()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you already have &lt;code&gt;Context&lt;/code&gt; with some mocked methods, you can use &lt;code&gt;new SPMockBuilder().wrapContext(preconfiguredContext)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you need raw SharedPreferences use new SPMockBuilder().createSharedPreferences().&lt;/p&gt;

&lt;p&gt;If thread safety is necessary for your test, then configure &lt;code&gt;SPMockBuilder.setThreadSafe(true)&lt;/code&gt;. It is false by default.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sample
&lt;/h2&gt;

&lt;p&gt;The sample below describes common code where logic uses preference class to check condition. In line 29 &lt;code&gt;SPMockBuilder&lt;/code&gt; creates a context for the preference unit tests. Simple, isn’t it?&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterPreferences&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SharedPreferences&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CounterPreferences&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@NonNull&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sharedPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSharedPreferences&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FILENAME&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MODE_PRIVATE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCounter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShowMessageLogic&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldShowMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;counterPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCounter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShowMessageLogicTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SPMockBuilder&lt;/span&gt; &lt;span class="n"&gt;spMockBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SPMockBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;CounterPreferences&lt;/span&gt; &lt;span class="n"&gt;counterPreferences&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ShowMessageLogic&lt;/span&gt; &lt;span class="n"&gt;showMessageLogic&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Before&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;counterPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterPreferences&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spMockBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;showMessageLogic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShowMessageLogic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counterPreferences&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;on42CounterItShouldShowMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;counterPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCounter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showMessageLogic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldShowMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onLessThan42ItShouldNotShowMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;counterPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCounter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertFalse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showMessageLogic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldShowMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;CounterPreferences&lt;/code&gt; interface and implement Java version for a test by yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn’t test CounterPreferences implementation&lt;/li&gt;
&lt;li&gt;Requires boilerplate code in tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Mockito:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn’t test CounterPreferences implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Robolectric:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has test startup delay for a few seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use instrumented test(not a unit test!):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires device for testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The library and sample code
&lt;/h2&gt;

&lt;p&gt;You can star the library and learn more info at:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IvanShafran" rel="noopener noreferrer"&gt;
        IvanShafran
      &lt;/a&gt; / &lt;a href="https://github.com/IvanShafran/shared-preferences-mock" rel="noopener noreferrer"&gt;
        shared-preferences-mock
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Library for unit testing shared preferences classes on Android
    &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;Shared Preferences Mock&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/IvanShafran/shared-preferences-mock/actions/workflows/code-health-check.yml/badge.svg"&gt;&lt;img src="https://github.com/IvanShafran/shared-preferences-mock/actions/workflows/code-health-check.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/IvanShafran/shared-preferences-mock" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b054b7777085992402e8cd4f011f7254d3181b06c9ef5b4e544649837224a2dd/68747470733a2f2f636f6465636f762e696f2f67682f4976616e5368616672616e2f7368617265642d707265666572656e6365732d6d6f636b2f6272616e63682f6d61696e2f67726170682f62616467652e737667" alt="codecov"&gt;&lt;/a&gt;
&lt;a href="https://central.sonatype.com/artifact/io.github.ivanshafran/shared-preferences-mock" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3523d3947d9f46e8eb783e54bcf31ba1d1a04898b95d5206a8910f1b5071b393/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f696f2e6769746875622e6976616e7368616672616e2f7368617265642d707265666572656e6365732d6d6f636b2e7376673f6c6162656c3d4d6176656e25323043656e7472616c" alt="Maven Central"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Shared preferences mock is a lightweight library that lets you increase coverage of unit tests and simplify code for them with one line of code.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Unit test on Android uses a framework mock where every method throws &lt;code&gt;UnsupportedOperationException&lt;/code&gt; or does nothing depending on your settings in Gradle &lt;code&gt;testOptions&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In unit tests, we mostly test business logic, which often depends on preferences. Therefore you should mock all classes which use &lt;code&gt;SharedPreferences&lt;/code&gt; inside. It is a lot of boilerplate in tests. Moreover, because of this limitation, we never test preferences class code. However, it also may have bugs.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Under the hood&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Under the hood the library implements &lt;code&gt;SharedPreferences&lt;/code&gt;, &lt;code&gt;Context.getSharedPreferences&lt;/code&gt; and &lt;code&gt;Context.deleteSharedPreferences&lt;/code&gt; in memory using only Java API. Check the differences in the differences paragraph.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Download&lt;/h2&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important: changed since 1.2.0&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Add it in your root build.gradle at the end of repositories:&lt;/p&gt;
&lt;div class="highlight highlight-source-groovy notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;allprojects {
    repositories {
        mavenCentral()
    }&lt;/pre&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/IvanShafran/shared-preferences-mock" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>android</category>
      <category>testing</category>
      <category>kotlin</category>
      <category>java</category>
    </item>
    <item>
      <title>Kotlin library development for Android/Java hints</title>
      <dc:creator>Ivan Shafran</dc:creator>
      <pubDate>Sun, 19 Feb 2023 20:58:41 +0000</pubDate>
      <link>https://forem.com/ivanshafran/kotlin-library-development-for-androidjava-hints-196a</link>
      <guid>https://forem.com/ivanshafran/kotlin-library-development-for-androidjava-hints-196a</guid>
      <description>&lt;h2&gt;
  
  
  Kotlin versions support
&lt;/h2&gt;

&lt;p&gt;When we develop a regular app for Android we can choose any Kotlin version. Some teams prefer to use the latest version in order to try new features and tooling improvements. Some teams wait some time before switching to a new version. It can be linked to a release cycle or waiting for community feedback on the last version.&lt;/p&gt;

&lt;p&gt;But when we develop a library on Kotlin we have to support both versions. Luckily Kotlin has good forward and backward compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backward compatibility
&lt;/h3&gt;

&lt;p&gt;It’s simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All binaries are backwards compatible, i.e. a newer compiler can read older binaries (e.g. 1.3 understands 1.0 through 1.2) [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But it only applies to fully stable APIs: std-lib, coroutines. Check [2] for the full list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forward compatibility
&lt;/h3&gt;

&lt;p&gt;There is such statement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Preferably (but we can’t guarantee it), the binary format is mostly forwards compatible with the next feature release, but not later ones (in the cases when new features are not used, e.g. 1.3 can understand most binaries from 1.4, but not 1.5). [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not very specific but in practice, my library which targets 1.3 works fine with 1.2 and even 1.1 std-lib without any changes. And it’s not working with 1.0 due to function reference feature which I used in the library code.&lt;/p&gt;

&lt;p&gt;But if we want more strict guarantees Kotlin compiler provides two useful flags: apiVersion and languageVersion.[3]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-language-version X.Y&lt;/code&gt; - compatibility mode for Kotlin language version X.Y, reports errors for all language features that came out later.[4]&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-api-version X.Y&lt;/code&gt; - compatibility mode for Kotlin API version X.Y, reports errors for all code using newer APIs from the Kotlin Standard Library (including the code generated by the compiler).[4]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Gradle I’ve configured it as following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jetbrains&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kotlin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gradle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;KotlinCompile&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;kotlinOptions&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.2"&lt;/span&gt;
        &lt;span class="n"&gt;languageVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.2"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you will probably see some compilation errors. In order to fix them, you should exclude new features usages and write useful extension/function/classes from 1.3 by yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Library development version
&lt;/h3&gt;

&lt;p&gt;You can use the latest Kotlin version in your development. Except for new language features (which we can’t use due to apiVersion and languageVersion) it also brings compiler and tooling improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dropping support of old versions
&lt;/h3&gt;

&lt;p&gt;In my opinion, it is not healthy to support old versions too long. I would recommend maintaining stability only one version back. Also, you could check release dates in artifacts repository to decide which versions to support [5].&lt;/p&gt;

&lt;h2&gt;
  
  
  Package structure and visibility
&lt;/h2&gt;

&lt;p&gt;A good library should hide all unnecessary classes, functions and properties from library users. Mostly you should prefer using private and internal modifiers for these situations.&lt;/p&gt;

&lt;p&gt;In addition, it is recommended to move all your not public code in the package with name “internal”:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also, by convention, packages named “internal” are not considered public API. [8]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Java compatibility
&lt;/h2&gt;

&lt;p&gt;In most cases, Kotlin does all work for you. It creates get/setfor properties for example.&lt;/p&gt;

&lt;p&gt;Currently, I’ve faced only one problem. If you declare a public property in companion from Java it is accessible via &lt;code&gt;Sample.Companion.INSTANCE&lt;/code&gt;. To fix that use &lt;code&gt;@JvmField&lt;/code&gt; annotation.&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;Sample&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@JvmField&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;INSTANCE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Sample&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;
  
  
  Kotlin dependency in pure Java project
&lt;/h2&gt;

&lt;p&gt;An Android library which is distributed as aar doesn’t include its dependency. Therefore in pure Java project library users will get &lt;code&gt;NoClassDefFoundError&lt;/code&gt; because your library depends on Kotlin std-lib. Thus you should write notices about that in the README file or docs.&lt;/p&gt;

&lt;p&gt;But people don’t like to read docs a lot. I would suggest one trick below. Please share in comments your opinion is it a good practice or not?&lt;/p&gt;

&lt;p&gt;Declare dependency checker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DependencyChecker&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ClassNotFoundException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Intrinsics exists in all std-lib versions&lt;/span&gt;
            &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kotlin.jvm.internal.Intrinsics"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClassNotFoundException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClassNotFoundException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Library depends on Kotlin std-lib.\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                 &lt;span class="s"&gt;"Add to dependencies: org.jetbrains.kotlin:kotlin-stdlib:x.y.z"&lt;/span&gt;
            &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check dependency in the static initializer block:&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;Sample&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&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="nc"&gt;DependencyChecker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&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;And if someone forgets to include Kotlin std-lib dependency he will get the solution in a crash log not just error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#evolving-the-binary-format" rel="noopener noreferrer"&gt;https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#evolving-the-binary-format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#evolving-the-binary-format" rel="noopener noreferrer"&gt;https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#evolving-the-binary-format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/using-gradle.html#attributes-common-for-jvm-and-js" rel="noopener noreferrer"&gt;https://kotlinlang.org/docs/reference/using-gradle.html#attributes-common-for-jvm-and-js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/evolution/compatibility-modes.html" rel="noopener noreferrer"&gt;https://kotlinlang.org/docs/reference/evolution/compatibility-modes.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib" rel="noopener noreferrer"&gt;https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio/write/java8-support" rel="noopener noreferrer"&gt;https://developer.android.com/studio/write/java8-support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/13550632/7958563" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/13550632/7958563&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#libraries" rel="noopener noreferrer"&gt;https://kotlinlang.org/docs/reference/evolution/kotlin-evolution.html#libraries&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Fragment: getContext vs requireContext</title>
      <dc:creator>Ivan Shafran</dc:creator>
      <pubDate>Fri, 17 Feb 2023 00:16:22 +0000</pubDate>
      <link>https://forem.com/ivanshafran/fragment-getcontext-vs-requirecontext-4149</link>
      <guid>https://forem.com/ivanshafran/fragment-getcontext-vs-requirecontext-4149</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR: use requireContext only when you are sure fragment is attached to its host(onResume, onViewCreated, etc).&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.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%2Faj3bcgbd1yptiv9mbwrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Faj3bcgbd1yptiv9mbwrc.png" alt="getContext vs requireContext"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Android team annotated some sdk methods with NonNull and Nullable in support library in 27.1.0. That change leads to warnings and errors like on the picture above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did they do this?
&lt;/h2&gt;

&lt;p&gt;At first sight it looks like adding more and more pain to android apps development. But actually this annotations help us to decrease crash rate.&lt;/p&gt;

&lt;p&gt;getContext can return null when fragment isn’t attached to its host. Let’s take a look at two examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Resources&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getResources&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First sample shows us that as long as fragment is attached to its host(in onResume for example) it’s safe to use getContext without nullability checking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;postDelayed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Runnable&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Resources&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getResources&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one reveals potential NullPointerException. If after 5 seconds fragment isn’t attached to host, app is crashed.&lt;/p&gt;

&lt;p&gt;So getContext Nullable annotation can prevent bugs like in the second sample. But it causes a useless warning in the first sample in Java and an error in Kotlin.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should we fix this?
&lt;/h2&gt;

&lt;p&gt;Firstly we should fix potentials bugs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find all places where fragment can be detached at some point of time&lt;/li&gt;
&lt;li&gt;Change code like in the sample below&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle null context scenario&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Old code with context&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In Kotlin:&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;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c1"&gt;// or if block&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally replace the rest with requireContext() which returns NonNull Context instead of getContext(). But keep in mind that requireContext() will throw an exception if fragment isn’t attached to its host. So you should use requireContext() with respect to fragment lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrong fixes
&lt;/h2&gt;

&lt;p&gt;In Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="c1"&gt;// requireContext() does the same&lt;/span&gt;
&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="c1"&gt;// Warning supressing only hides the real problem&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@NonNull&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onResume&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;//noinspection ConstantConditions&lt;/span&gt;
    &lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Kotlin:&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;// It is similar to requireContext() but with NullPointerException&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other similar methods
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;getActivity vs requireActivity&lt;/li&gt;
&lt;li&gt;getHost vs requireHost&lt;/li&gt;
&lt;li&gt;getFragmentManager vs requireFragmentManager&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>java</category>
    </item>
  </channel>
</rss>
