Understanding Custom Test Rule with Coroutines Examples

İrem Çelikbilek
Delivery Hero Tech Hub
5 min readNov 26, 2021
Photo by Highlight ID on Unsplash

Normally, when we write applications, we write some classes and methods in order not to repeat the code. The same is true for the tests we wrote for our app. I’m sure you’ve noticed that some code repeats itself multiple times in testing. Here we use rule to avoid this code duplication when writing tests.

Rules can do everything that could be done previously with methods annotated with @Before, @After, @BeforeClass, or @AfterClass, but they are more powerful, and more easily shared between projects and classes.

Test Execution Diagram

Rules are the most effective tool to prevent code duplication. Rules are the most effective way to change the behavior of all tests in a class. I think we need to understand how these tools work before we move on to writing rules. The diagram above sums it up best. As shown in the diagram, the rules (if you are not using class rule) repeat for each test method. They don’t work just once!

Now we know why we should use rule. Now, back to our original question. How do I write a rule?

We have two methods to write a custom test rule:

  1. Inheriting the Rule class from TestRule.
  2. Inheriting the Rule class from TestWatcher.

First Option : Using the TestRule

TestRule is an interface and we override the apply method in it. This method takes two parameters, statement and description, and returns Statement. Statement represents the actions to be performed during the runtime. So our tests. Description, on the other hand, allows us to get feedback about the tests to be run or run.

We said that the Apply method returns us a Statement. Statement also override the evaluate method in itself. So what’s the use? “It enables our test methods to run.” This part is important because we will be dealing with the before and after of this method. If we understand the logic of the TestRule interface, we can move on to our example.

Test rule for use coroutine on test class

First we implement the TestRule interface. Then we override the apply method. The base?.evaluate() method will make the test run. So what do we do before this?

Our purpose for writing this rule is to be able to use the coroutine scope while testing the classes we use coroutines for. Because suspend functions can either be called in suspend fun or they can be called in coroutine scope. For this, the runBlockingTest function will be activated. It will ensure that the suspend fun we provide takes place in the test coroutine scope we created. Also this is similar to runBlocking but it will immediately progress past delays and into launch and async blocks. You can use this to write tests that execute in the presence of calls to delay without causing your test to take extra time.

Also, another problem with testing coroutines is the “main” thread. Because we don’t have access to main thread in tests. That’s why we use the Dispatchers.setMain() method. This method allows us to change the dispatcher. So we can exit the main thread and switch to our own test dispatcher. We explained what we need to do before calling the test. What about after the test?

I said that we could not access the main thread (Dispatchers.Main) in our test. We needed to create our own test dispatcher to run this test. But now, the evaluate() method was called and our test was finished. This means we can now return to our main thread.

We also call the cleanUpTestCoroutines() method. This method throws UncompletedCoroutinesError if there is still an active coroutine and then the test will fails. In this way, our test gives more efficient results.

I hope you understand the logic of CoroutineTestRule.

Next, we have second method…

Second Option : Using the TestWatcher

Actually, we’re not going to do anything very different from the example above. Because it already implements TestRule in TextWatcher. Using TestWatcher helps further simplify and modularize our code. To understand this more clearly, let’s examine how TestWatcher works in the background.

As seen in the code, the logic is simple. In TestWatcher, methods are called that we can override for every possible scenario. We don’t need to do this manually. Everything is automated. Now we know what’s going on in the background. Now we can move on to our example.

Test rule with TestWatcher for use coroutine on test class

The Last Part : Implementation in Our Test Class

Have you ever wondered why you only write @Rule in java while @get:Rule in kotlin? If you just write @Rule in kotlin you will get this error:

JUnit allows providing rules through a test class field or a getter method. So @Rule annotation is in Kotlin a property though, which JUnit won’t recognize.

As an alternative solution, you can add @JvmField next to @Rule annotation.

Conclusion

Don’t be lazy use rule :)

So which one should i use?

In fact, both uses are the same as logic. TestWatcher makes things easy for you because it gives you everything ready-made. However, if you want to be in control of the rule you will write, you should use TestRule.

Thank you for reading. I hope it was helpful.

References

Note: This blog is private

Note: Also you can see that

https://developer.android.com/reference/androidx/arch/core/executor/testing/InstantTaskExecutorRule

--

--