Let me take you on the journey of rescuing our father from the belly of the beast. Roughly speaking.

Let’s talk about JUnit. It’s a helpful little framework for running your tests on the JVM. It plays nicely with tons of other tools: your IDE, your build system, your CI. I’m running out of imagination, and hope you get the point - it’s useful. But it’s also archaic, and sometimes willfully blind.

You know the @Rule s, right? Here’s an example straight from the docs:

public static class HasTempFolder { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { File createdFile = folder.newFile("myfile.txt"); File createdFolder = folder.newFolder("subfolder"); // ... } }

What it does is it executes some extra code before and/or after each of your test. TemporaryFolder rule cleans up after the tests, no matter if they pass or fail. A lot of third party libraries provide the @Rule s to help you test your code.

Execution order

But here’s the deal: you don’t have much control over when your TestRule executes it’s logic. Take a look at ActivityTestRule from Android Testing Support Library. The rule itself is fine and dandy, but when you try to execute some code before your Activity (Android GUI) is launched - you’re fucked. @Before won’t help you. Running some code right at the start of the test won’t help you either - the @Rule gets to run first. You can use this constructor and call rule.launchActivity(Intent) manually but that makes the rule just that much ceremonial. And if you want to run your @Before code before the rule - you have to override a method! This gets really ugly really fast.

Composability

You also can’t have two @Rule s in your test. You just can’t, because of the execution order problem. They’re hard to compose. JUnit has the workaround for it - a RuleChain. It’s fairly declarative but you still have no control over the execution order, really. Let’s try to compose the infamous ActivityTestRule with the MockWebServer. The objective is simple: we run the server first, so that our GUI can connect to it immediately:

public static class UseRuleChain { @Rule public RuleChain chain = RuleChain.outerRule(new MockWebServer()) .around(new ActivityTestRule<>(MainActivity.class)); }

Will this work? Probably not, since we need to enqueue some responses first. We’re stuck again - now we need to execute the code between the rules. You can still work around this with launchActivity but I wouldn’t bother. We can do better.

Kotlin

Let’s look at what TestRule is fundamentally. It’s a function that takes a Statement (a block of code), a Description (whatever that is), and outputs a new Statement (a block of code). A Statement can be evaluated to produce an effect. Let’s try to encode that in Kotlin. I’ll represent a Statement with the () -> Unit function type:

fun testRule(base: () -> Unit, description: String): () -> Unit = TODO()

We can put the base argument at the end, so that we can pass the lambda out of the parentheses:

fun testRule(description: String, base: () -> Unit): () -> Unit = TODO()

And take advantage of that:

@Test fun someTest() { codeBeforeTheRules() testRule("rule numba one") { codeBetweenTheRules() testRule("rule numba two") { testingCode() } } }

Doesn’t this look nice? Let’s write a function that would convert any rule into this kind of DSL:

fun statement(f: () -> Unit): Statement = object : Statement() { override fun evaluate() = f() } fun <R : TestRule> R.evaluate(f: (R) -> Unit): Unit = apply(statement { f(this) }, Description.EMPTY).evaluate() @Test fun composableRules() { MockWebServer().evaluate { it.enqueue(makeResponse()) ActivityTestRule(MainActivity::class.java).evaluate { // test my GUI } } }

We can make it even better - lets wrap the rule creation into beautiful DSL functions, and combine them with Kotlin Robots:

fun mockWebServer(f: MockWebServer.() -> Unit): Unit = MockWebServer().evaluate(f) inline fun <reified A : Activity> activity(crossinline test: ActivityTestRule<A>.() -> Unit): Unit = ActivityTestRule(A::class.java).evaluate(test) @Test fun beautifulRules() { mockWebServer { enqueue(makeResponse()) activity<DetailsActivity> { details { edit { name("Adel") bio("a father, a mother, a friend") } save { savedSuccessfully() } } } } }

This way we get much better readability and a total control of the execution order of the rules. It solves their biggest problem and allows me to go home earlier (I’m working remotely, but that doesn’t really matter). You gotta love the creative platform Kotlin provides us to solve some of the most frustrating problems in the day to day work of the Android/JVM developer.

I’m gonna go clean my room now.