JUnit 5 and Android Architecture Components

Lisa Watkins
Code With Lisa
Published in
3 min readMar 27, 2019

Oh, JUnit tests. I’ve grown quite stubborn to insist on writing JUnit tests over Instrumentation tests (when possible, and when reasonable). If you’ve developed a few Android applications, we probably have this in common.

It’s an exciting time for Kotlin developers to adopt JUnit 5. Here’s a brief, and probably NOT all-inclusive list:

  • JUnit 5 has added advanced assertions for working with lambdas
  • You can now specify to only create ONE instance of the test class per test method (this one particularly excites me, I could shout it from the mountain top)
  • You can now “nest” test methods that make sense together with the @Nest annotation

If you’re not already using JUnit 5, maybe my brief list just convinced you to give it a shot. Great! Let’s talk more about the architecture of your app. Are you using Android Architecture Components? Maybe LiveData?

The following test method is verifying that PetRepository#nextPet() is grabbing a LiveData object that contains a Pet I retrieved from the network. (Oh yeah, if you haven’t heard, I’m building an app called Pawsitive — so I talk about dogs and cats and animals a lot in the blog.)

Side bar: I’m also using Mockk and Kotlin Coroutines and lots of fun stuff but you’ll have to check out a different article (coming soon!) for more details on that.

To recap, in this test I am concerned about making sure that my LiveData object has what I expect it to have. I am using observerForever{..} so that I can subscribe to the LiveData object captured in result.

This seems fine, until —

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.at android.os.Looper.getMainLooper(Looper.java)

Oof. I’m in a JUnit test! This is bringing back nightmares of “Tests I’ve Struggled Writing Before” past. Of course, the JVM doesn’t know anything about the main thread (or any other Android dependency for that matter).

I promised you this 2 minute read would save you some time — so here it is…

Step 1: Add this dependency to your app’s build.gradle:

testImplementation “android.arch.core:core-testing:$version”

Step 2: Now, write this Kotlin extension. Stick it somewhere that makes sense in your test package.

Step 3: Add this line above your test class:

@ExtendWith(InstantExecutorExtension::class)

If you came to this article to just catch a fish, you can stop reading right here. If you came to this article to learn to fish, keep reading for a “why” behind this fix.

So, why did this exception happen in the first place? If you’re new to Android development, I’ll just give you a quick review.

LiveData has some methods that make assertions you are running on the main thread. This is needed for Android world, but if you’re running JUnit tests with only the JVM, this check is going to result in a crash. We don’t have any knowledge about android.os.Looper in the JVM. This means that somehow we must figure out how to interact with LiveData without using the main thread.

The architecture components team was kind enough to provide a way to do this — with ArchTaskExecutor.

If we were using JUnit 4, we would have simply added a Rule to our test class. The reason that we had to make this Kotlin extension with JUnit 5 is because JUnit 5 bundled up the concept of Runner, TestRule, and MethodRule all under one API called Extension. Extensions are registered with a class much like JUnit 4 Runners — with a class annotation. This annotation is @ExtendWith(...) .

The extension we added above sets a TaskExecutor delegate on ArchTaskExecutor.

Check out this line from the code above:

override fun isMainThread(): Boolean = true

We are simply faking out that we are on the main thread. We set this delegate before each test runs, and then we clean it up right after to avoid any unintended side effects.

No magic, just some fish. Err — code.

As always, happy coding!

--

--

Lisa Watkins
Code With Lisa

Engineer, Activist, Cat Lady. Mobile engineering @ Lyft.