JUnit Annotations: Android | Erselan Khan
Today, we’ll go through some of the most important JUnit4 annotations in Android. But, before you go any further, I recommend that you read this article for a better understanding:
Now, write down some key annotations and go over them one by one:
- @Test
- @Before
- @After
- @Rule
@Test: This annotation is used with methods/functions to allow us to run test cases within the methods/functions block.
Want to check spelling mistakes on your documents? download Spelling Checker from the Play Store.
Example:
/*
test case for empty email address
*/
@Test
fun `empty email address returns false`() {
val resultValue = FieldValidation.isValidLoginFields(
"",
"some password"
)
assertThat(resultValue).isFalse()
}
@Before: If we wish to run some statements as preconditions before each test case, we use this annotation. This annotation can be used for database initialization, object initialization, and context initialization, etc
@After: If we want to run a statement after each test case, we use this annotation. For example, we can use this annotation to close a database, reset an object or context, and so on.
Example: Code example for both @Before and @After annotation.
private lateinit var userDatabase: UserDatabase
private lateinit var userDao: UserDao
// execute before every test case
@Before
fun setup() {
val context = ApplicationProvider.getApplicationContext<Context>()
userDatabase = Room.inMemoryDatabaseBuilder(context, UserDatabase::class.java)
.allowMainThreadQueries()
.build()
userDao = userDatabase.userDao()
}
// execute after every test case
@After
fun teardown() {
userDatabase.close()
}
/*
test case to insert user in room database
*/
@Test
fun insertUser() = runBlockingTest {
val userEntity = UserEntity("Erselan Khan", 26, "Some Random Email", 1)
userDao.insert(userEntity)
val users = userDao.getUsersList().getOrAwaitValue()
assertThat(users).contains(userEntity)
}
In the above example, we initialize the UserDatabase and UserDao before each test case and close the database after each test case.
@Rule: We use this annotation when we want to execute some statement before and after each test case like @Before and @After annotations. But why do we need @Before and @After annotations, or why do we need to build a @Rule? So, let me walk you through the examples and explain which annotations are best for different types of circumstances. To begin, we’ll look at @Rule annotation.
Imagine you have two or more test classes that require the same Setup(Before) and TearDown(After). We could have them as a TestRule and quickly apply them to both test classes instead of repeating the code.
Example: Code example for @Rule annotation.
class CompareValuesTestRule(var compareValuesTest: CompareValuesTest) : TestRule {
override fun apply(base: Statement, description: Description?): Statement {
return CustomStatement(base, compareValuesTest)
}
class CustomStatement(private val base: Statement, private var compareValuesTest: CompareValuesTest) : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
// Add something you do before
compareValuesTest.compareValues = CompareValues()
try {
base.evaluate() // This executes your tests
} finally {
// Add something you do after test
compareValuesTest.compareValues = null
}
}
}
}
Here we have CompareValuesTestRule class which takes the CompareValuesTest as a parameter to initialize compareValues object before each test case and reset the value of compareValues to null after each test case.
class CompareValuesTest {
@get:Rule
val compareValuesTestRule = CompareValuesTestRule(this)
var compareValues: CompareValues? = null
/*
returns true when strings are same
*/
@Test
fun checkStringsAreSame_returnsTrue() {
val context = ApplicationProvider.getApplicationContext<Context>()
// Here we are using the compareValues object
// which we already initialized on our CompareValuesTestRule class before each test case
val value = compareValues?.comparesStrings(context, R.string.enter_email, "Enter Email")
assertThat(value).isTrue()
}
/*
returns false when strings are different
*/
@Test
fun checkStringsAreDifferent_returnsFalse() {
val context = ApplicationProvider.getApplicationContext<Context>()
// Here we are using the compareValues object
// which we already initialized on our CompareValuesTestRule class before each test case
val value = compareValues?.comparesStrings(context, R.string.enter_email, "Erselan Khan")
assertThat(value).isFalse()
}
}
Because we had specified the rule to initialize compareValues before each test case on our CompareValuesTestRule class, we are using the compareValues object on each test case. Multiple test classes can now utilize the same CompareValuesTestRule class. Additionally, here we are using the @get:Rule instead of @Rule because we are using Kotlin for writing our test case. If you use @Rule with Kotlin, we get this error:
org.junit.runners.model.InvalidTestClassError: Invalid test class ‘com.systems.unittesting.utils.CompareValuesTest’:
1. The @Rule ‘compareValuesTestRule’ must be public.
So if you want to use @Rule annotation, then you also have to use @JvmField annotation like this:
@Rule @JvmField
val compareValuesTestRule = CompareValuesTestRule(this)
That’s it for now. I will cover more topics on testing in my upcoming articles.
Show your love by sharing this article with your fellow developers.
(Again, the source for this demo is on https://github.com/arsalankhan994/unit-testing-step-by-step/tree/implement-test-rule. Follow me for more content about Android and other technologies. If you have any questions, go ahead and ask me here or email me at arsalankhan994@gmail.com and I’ll do my best to respond.)