How to test protected function

In Java protected members are also visible in the whole package and thus could be tested right away.
In Kotlin however, protected members are only visible in subclasses.

How are they supposed to be tested in unit tests?
From my point of view, protected members are part of the API and really should be tested.

2 Likes

Subclass your class and call super.

That works, of course.

But that means your test class needs to extend the class under test which is quite unusual in my opinion.

As you said, protected functions are part of the API, so it makes sense to test them.
IMO, it makes sense to test them the way they are going to be used.

1 Like

It would look like this:

open class A {
    protected fun f() = 3
}

class ATest : A() {
    @Test fun testF() {
        val testee = ATest()
        assertEquals(3,testee.f())
    }
}

Maybe its just me, but I find it strange that the “testee” is the test class itself…

As far as I know nothing is stopping you from doing something like this:

open class A {
    protected fun f() = 3
}

class B: A() {
    public fun fPublic() = f()
}

class ATest {
    @Test fun testF() {
        val testee = B()
        assertEquals(3,testee.fPublic())
    }
}

which gives you sepration of tested code form code of the test.

Well, to be honest, thats exactly how I did it.
But I didn’t like it, creating a wrapper class and a wrapper function.
So I asked myself, if I was missing something…

Thanks anyway for all your comments!

I’m having this same issue. I understand that you can extend the class when it’s open or abstract (as shown in comments above), but what about when it’s final?

Sure, you can change your class to be open because you want to test the protected methods inside it, but then your code architecture is influenced by the tests, which I don’t like. The whole reason why Kotlin classes are not open by default is so that we can make them open only when it makes sense, and for me, it doesn’t make sense to make it open just to test it.

Is there any other option?

If you have a final class, protected members don’t make any sense (and I hope are rejected by the compiler). If you want to test internal methods, that’s another matter.

Hi Hugh,

I agree with that in most cases, but I have a scenario where this actually makes sense, e.g.:

abstract class A {
    public fun doStuff() {
        doSomething()
    }

    protected abstract fun doSomething()
}

class B : A() {
    override fun doSomething() {
        ...
    }
}

and what I want is to create a unit test for B.doSomething()

I guess one thing you could do is use the all open compiler plugin while testing and disabling it when deploying. It should be possible to set something like that up using gradle/maven. The only problem is that you no longer get any errors when inheriting from a sealed class during development and testing. Not sure if this is really the best thing to do, but if you want to check internal methods thats the only idea I have.

1 Like

Thanks for the recommendation Wasabi375! I was not aware of that plugin. I will look into it in more detail when I have time, but if it’s too much work to set up only for testing (without affecting the development compilation) then I probably won’t use it.

For the moment what I did is set the protected methods I need to unit test as internal and added a TODO so I can make them protected once I find a better solution. It’d be great to get some official support for this, maybe a plugin like the one you mentioned but that changes the visibility of methods or classes only for testing.

Ah, I see: you want to test final overrides of a protected method. In that sort of case, I wouldn’t try to test the protected method directly. Instead, I’d do two things.

  1. Write a test which uses a spy subclass to check that the base class is calling the protected method as it should. (For example, if the base class should cache the result, then base method calls after the first shouldn’t reach the subclass.)
  2. Write an abstract test for the base class and override it for each subclass which needs to be tested. The test subclasses would change the setup and/or add more tests to cover the effects of the method overrides.

Alternatively, change from inheritance to composition - pull out the overridable parts into a Strategy object, and test those Strategies as separate classes.

Thanks for the help @HughG :slight_smile:

Would you mind giving a short example of point number 2? I’m not sure I understand what you meant exactly and a short code example would be greatly appreciated.

Changing from inheritance to composition would certainly make testing easier. Unfortunately, given the nature of the relationship between the classes, using composition would make the code more complicated and it’d make less logical sense.

You can change the visibility when overriding - you can’t change it to internal (apparently you can’t make visibility more restricted), but you can change it to public, so this would work:

class B : A() {
    public override fun doSomething() {
        ...
    }
}

This will be fine in terms of encapsulation if class B already has restricted visibility so can’t be accessed from outside your module.

@theartofme that’s actually not good from my point of view, as I’d still be exposing this method to the rest of my module, which is not something I want to do (if that was the case I’d just make the original method internal). I want the protected method to stay with the same visibility and not be accessible from outside the base or child classes, except for testing.

Any official updates on this? I feel like we’re not dealing with the elephant in the room here.
All of the proposed solutions imply modifying production code for testing, or resorting to external solutions (AllOpen) which IMHO are pretty dangerous to use on big projects with several developers working on the same codebase.

Right now, this limitation (or change of spec, as it might be more correctly defined) is getting in the way of writing unit tests quickly and effectively, especially in the case of final classes with protected functions (which is perfectly legal and reasonable when they override protected functions from their open or abstract superclasses). On Android, one could use @VisibleForTesting to get static analysis to help with not calling previously-protected functions from production code; that’s still not great with larger classes, though.
I can easily see devs giving up on using proper logic encapsulation for the sake of being able to write tests more quickly. This can’t lead to anything good :slight_smile:

1 Like

I have to say I’m also disappointed that this doesn’t exist yet. Kotlin is in need of a package-private modifier.

Numerous articles have been written on the matter.

package-private modifier is just a fake. Nothing stops developer from creating same package structure in his project and to get full access to all package protected members.

Kotlin have internal modifier, which works fine within Kotlin code, but I have problem to access those functions from Java unit tests.

“Fake” or not, it’s very useful. How many people actually suffered from the supposed “fakeness” of package visibility in Java? I’d dare to guess that that number is very small.

Just because the feature doesn’t actively enforce some kind of code security doesn’t mean it isn’t really useful.

As for myself, my Kotlin code mostly uses public visibility anyway, since I’m fed up with having to deal with the limitations of the existing mechanisms.