Kotlin - Unit Testing Classes Without Leaking Public API!
Tags: kotlin, jvm, java, testing
Published: 2020-12-05 08:34:23

Introduction
============

Kotlin is an amazing new up-and-coming language. It's [very actively developed](https://blog.jetbrains.com/kotlin/category/releases/) and has a ton of features that make it very appealing.
It's been steadily gaining market share ever since Google [added Android Development support for it](https://techcrunch.com/2017/05/17/google-makes-kotlin-a-first-class-language-for-writing-android-apps/) (back in 2017) and [made it the preferred language for such development](https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/) exactly one year ago (May 2019)

For any Java developer coming into the language, spoiler alert, there is one big surprise that awaits you --- the `package-private` visibility modifier is missing. It doesn't exist.

Access Modifiers
================

An *access modifier* is the way to set the accessibility of a class/method/variable in object-oriented languages. It's instrumental in facilitating [proper encapsulation of components](https://medium.com/free-code-camp/a-short-overview-of-object-oriented-software-design-c7aa0a622c83).

Java
----

In Java, we have four access modifiers for any methods inside a class:

-   `void doSomething()`- the default modifier, we call this package-private. Any such declarations are only visible within the same package (hence the name, private for the package)
-   `private void doSomething()` --- the private modifier, declarations of which are visible only within the same class
-   `protected void doSomething()` --- declarations only visible within the package or all subclasses.
-   `public void doSomething()` --- declarations are visible everywhere.

This wide choice of options allows us to tightly-control what our class exposes. It also gives us greater control over what constitutes a unit of testable code and what isn't. See the following crude example:
```java
package doer.something;

/**
 * Does something.
 */
public class JSomethingDoer {

  private int motivationLevel;

  /**
   * @param motivationLevel a 0-100 integer, denoting how motivated the #{@link JSomethingDoer} is.
   */
  public JSomethingDoer(int motivationLevel) {
    this.motivationLevel = motivationLevel;
  }

  public void doSomething() {
    doOneLittleThing();
    maybeDoSecondLittleThing();
  }

  void doOneLittleThing() {
    System.out.println("Doing one thing.");
  }

  // package-private for testing
  void maybeDoSecondLittleThing() {
    if (feelingAmbitious()) {
      doSecondLittleThing();
    }
  }

  private void doSecondLittleThing() {
    System.out.println("We're overachievers, doing a second thing!!!");
  }

  private boolean feelingAmbitious() {
    return motivationLevel > 90;
  }
}
```

We can only directly test three of the five methods above. Because `doSecondLittleThing()` and `feelingAmbitious()` are private, our unit tests literally cannot call those methods, hence we cannot test them directly.

The only way to test them is through the package-private method `maybeDoSecondLittleThing()` , which can be called in our unit tests because they're in the same package as the class they're testing.

```java
package doer.something;

import org.junit.Before;
import org.junit.Test;

public class JSomethingDoerTest {

  private JSomethingDoer doer;

  @Before
  public void setUp() {
    doer = new JSomethingDoer(11);
  }

  @Test
  public void Test_doSomething() {
    doer.doSomething();
    // assert
  }

  @Test
  public void Test_doOneLittleThing() {
    doer.doOneLittleThing();
    // assert
  }

  @Test
  public void Test_maybeDoSecondLittleThing() {
    doer.maybeDoSecondLittleThing();
    // assert
  }
}
```

The package-private modifier comes very useful in this example, because it allows us to test the `maybeDoSecondLittleThing()` method directly!
Testing would be more cumbersome if we had to test all code paths of the `maybeDoSecondLittleThing()` method by calling the `public doSomething() `method.

The package-private modifier also ensures that we don't leak any such internal methods to packages outside of this one.

![Java IDE Example](https://dev-to-uploads.s3.amazonaws.com/i/pu6947srdf61w4rel4ha.png)
_Notice the different package `doer` in the Main class._

Kotlin
------

Kotlin is a bit weirder in this regard. It also has four modifiers:

-   `private fun doSomething()` --- vanilla private modifier
-   `protected fun doSomething()` --- the same as Java's protected --- only visible within this class and subclasses.
-   `public fun doSomething()` --- visible everywhere

These three are pretty standard. I want to focus on the fourth one:

-   `internal fun doSomething()` --- Internal declarations are visible anywhere inside *the same module*.

This is as good as public inside the same module, in that sense. Many small projects have no need for multiple modules, but they're likely to have multiple packages.

There is basically no way to limit access to a Kotlin method to only be within the same package.

What does this mean? It means that we either need to make our methods private, thus making our testing life harder, or make them public, thus exposing unnecessary API to components in the same module.

Our same example from above, again in the `doer.something` package:
```kotlin
package doer.something

/**
 * Does something.
 * @param motivationLevel a 0-100 integer, denoting how motivated the #[SomethingDoer] is.
 */
open class SomethingDoer(private val motivationLevel: Int) {
  fun doSomething() {
    doOneLittleThing()
    maybeDoSecondLittleThing()
  }

  protected fun doOneLittleThing() {
    println("Doing one thing.")
  }

  private fun maybeDoSecondLittleThing() {
    if (feelingAmbitious()) {
      doSecondLittleThing()
    }
  }

  private fun doSecondLittleThing() {
    println("We're overachievers, doing a second thing!!!")
  }

  private fun feelingAmbitious(): Boolean {
    return motivationLevel > 90
  }
}
```

But this time, from the `doer` package, we can call every method:

![IDE Kotlin Example](https://dev-to-uploads.s3.amazonaws.com/i/x9livjk8obum3yj925hv.png)

How do we solve it?
===================

Solution 1 --- Refactor!
----------------------

![Us refactoring our code](https://dev-to-uploads.s3.amazonaws.com/i/9hw2gmbot9yrsuqxfln6.jpeg)

It seems to be [commonly believed](https://stackoverflow.com/questions/34571/how-do-i-test-a-private-function-or-a-class-that-has-private-methods-fields-or) that we shouldn't test methods that are/should be private. The idea being that:

-   all private methods are reachable by the public methods
-   the public methods expose the interface/contract of a class
-   private methods are implementation details, and we want to test the class' functionality, not implementation

While that makes sense in theory, reality is not as black-and-white.

Regardless, you should treat any need to test non-public methods as a code smell.
Re-evaluate your design and rethink whether it makes sense to refactor the logic around such that you get the same test coverage by testing public interface contracts, rather than implementation details.

In some cases, though, it is cleaner and more maintainable to get full test coverage by testing each tiny private method code thoroughly, rather than testing each conditional branch via public methods or creating unnecessary boilerplate (e.g additional classes).

Solution 2 --- Reflection
-----------------------

![Reflection](https://dev-to-uploads.s3.amazonaws.com/i/oxdmfkppf6kqn3df6gfs.jpeg)

An alternative solution is to keep the methods `private` and use the language's reflection features to call into said private methods.

Add a dependency on the `kotlin-reflect` library and code away!

```kotlin
  fun callPrivate(objectInstance: Any, methodName: String, vararg args: Any?): Any? {
    val privateMethod: KFunction<*>? =
        objectInstance::class.members.find { t -> return@find t.name == methodName } as KFunction<*>?

    val argList = args.toMutableList()
    (argList as ArrayList).add(0, objectInstance)
    val argArr = argList.toArray()

    if (privateMethod?.javaMethod?.trySetAccessible()!!) {
      privateMethod?.apply {
        return call(*argArr)
      } ?: throw NoSuchMethodException("Method $methodName does not exist in ${objectInstance::class.qualifiedName}")
    } else throw IllegalAccessException("Method $methodName could not be made accessible")

    return null
  }
```
Woah.
![Code and tests](https://dev-to-uploads.s3.amazonaws.com/i/x96nezs3swf8amucziik.png)

Here, our `callPrivate()` test helper is the star of the show! It allows us to call into private methods, albeit in a funky way.
We need to specify the method name by a string, hence we lose all type checking guarantees. This results in fragile code that's also hard to read.

We can leverage a [compiler plugin](https://docs.oracle.com/javase/8/docs/jdk/api/javac/tree/com/sun/source/util/Plugin.html), like [Manifold](https://github.com/manifold-systems/manifold), to get type-safe meta-programming. But you can argue that takes it a bit too far, as it introduces a large dependency that involves a lot more meta-programming behind the scenes.


Solution 3 --- Open the class, protect the methods and test a wrapper!
--------------------------------------------------------------------

A third, perhaps preferable way to solve this is with a bit of subclassing work.

First, we need to make our class [open for extension](https://blog.mindorks.com/understanding-open-keyword-in-kotlin) via the `open` keyword. Then, we need to make our private methods `protected`.
```kotlin
open class SomethingDoer(private val motivationLevel: Int) {
  fun doSomething() {
    doOneLittleThing()
    maybeDoSecondLittleThing()
  }

  protected fun doOneLittleThing() {
    println("Doing one thing.")
  }
}
```

Finally, we can create a subclass inside the test class whose sole goal is to expose the protected method directly.

```kotlin
class SomethingDoerTest {

  class TestSomethingDoer(motivationLevel: Int) : SomethingDoer(motivationLevel) {
    fun testDoOneLittleThing() {
      super.doOneLittleThing()
    }
  }
  private lateinit var doer: SomethingDoer
  private lateinit var doerWrapper: TestSomethingDoer

  @Before
  fun setUp() {
    doer = SomethingDoer(99)
    doerWrapper = TestSomethingDoer(99)
  }

  @Test
  fun doSomething() {
    doer.doSomething()
    // assert
  }

  @Test
  fun Test_doOneLittleThing() {
    doerWrapper.testDoOneLittleThing()
    // assert
  }
}
```

This is yet another imperfect solution to the problem which results in more boilerplate and a non-totally-private method.

It is better than the reflection approach in that it's type-safe, therefore less prone to breakage and significantly easier to maintain.

Summary
=======

In this short article we went over the access modifiers of both Java and Kotlin and saw where Kotlin comes short. We proposed some solutions to Kotlin's lack of the package-private modifier with relation to testing, all with sufficient code examples!

Speaking of which, the code for this article live on GitHub --- [here](https://github.com/stanislavkozlovski/unit-test-kt).

References
----------

This problem has been hit by many, unsurprisingly. It makes sense given that most people come from the same background --- we miss our good old Java features 😭

-   Kotlin Discussion Forum --- <https://discuss.kotlinlang.org/t/kotlin-to-support-package-protected-visibility/1544>; [https://discuss.kotlinlang.org/t/another-call-for-package-private-visibility/9577](https://discuss.kotlinlang.org/t/another-call-for-package-private-visibility/9577/3)
-   KT Issue on the matter --- <https://youtrack.jetbrains.com/issue/KT-29227>
-   Another take on Medium --- <https://medium.com/virtuslab/on-the-missing-package-private-or-why-java-is-better-than-kotlin-in-this-regard-4a1c9ecbe40c>