Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create parametrized test cases in JUnit 4 and Kotlin for Android?

I need something similar to C# NUnit TestCase scenario with fixed expected results.

I've already found this question on SO which has great solutions but they are for Java only and as suggested in some of the answers I followed this tutorial, but simply converting it to Kotlin doesn't work.

@RunWith(value = Parameterized.class)
public class EmailIdValidatorTest {

    private String emailId;
    private boolean expected;

    public EmailIdValidatorTest(String emailId, boolean expected) {
        this.emailId = emailId;
        this.expected = expected;
    }
    @Parameterized.Parameters(name= "{index}: isValid({0})={1}")
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][]{
                        {"[email protected]", true},
                        {"[email protected]", true},
                        {"[email protected]", true},
                        {"mary@testdomaindotcom", false},
                        {"mary-smith@testdomain", false},
                        {"testdomain.com", false}
                }
        );
    }
    @Test
    public void testIsValidEmailId() throws Exception {
        boolean actual= EmailIdUtility.isValid(emailId);
        assertThat(actual, is(equalTo(expected)));
    }
}

So what is the correct way to write Kotlin parametrized unit tests with JUnit?

like image 568
MatPag Avatar asked Mar 20 '19 11:03

MatPag


People also ask

How do you write parameterized test cases in JUnit?

There are five steps that you need to follow to create a parameterized test. Annotate test class with @RunWith(Parameterized. class). Create a public static method annotated with @Parameters that returns a Collection of Objects (as Array) as test data set.

What are parameterized tests used for in JUnit?

JUnit 5, the next generation of JUnit, facilitates writing developer tests with shiny new features. One such feature is parameterized tests. This feature enables us to execute a single test method multiple times with different parameters.

What is parameterization in testing?

Test parameterization is a type of data-driven testing that allows you to execute the same test, multiple times using different parameters. Xray Cloud has a parameterized tests feature, that executes the same test with different input values, multiple times, without ever having to clone or replicate it.


2 Answers

Following this amazing tutorial, we can implement it in Kotlin language in this way:

First of all convert the EmailIdUtility class into Kotlin:

object EmailIdUtility {
    fun isValid(email: String): Boolean {
        val regex =
            "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$"
        val pattern = Pattern.compile(regex)
        val m = pattern.matcher(email)
        return m.matches()
    }
}

Then convert the EmailIdValidatorTest into Kotlin

@RunWith(value = Parameterized::class)
class EmailIdValidatorTest(
    private val email: String,
    private val expected: Boolean)
{
    @Test
    fun testIsValidEmailId() {
        val actual = EmailIdUtility.isValid(email)
        assertEquals(expected, actual)
    }

    companion object {
        @JvmStatic
        @Parameterized.Parameters(name = "{index}: isValid({0})={1}")
        fun data(): Iterable<Array<Any>> {
            return arrayListOf(
                arrayOf("[email protected]", true),
                arrayOf("[email protected]", true),
                arrayOf("[email protected]", true),
                arrayOf("mary@testdomaindotcom", false),
                arrayOf("mary-smith@testdomain", false),
                arrayOf("testdomain.com", false)
            ).toList()
        }
    }
}

Remember to add @JvmStatic in the data() method, otherwise you will get the error: java.lang.Exception: No public static parameters method on class com.example.testapp.dev.EmailIdValidatorTest


EASIER WAY

If you can use another library (working in Android too) I suggest you to add JUnitParams to your test dependencies, in Android it could be:

testImplementation  "pl.pragmatists:JUnitParams:1.1.1"

Then you could convert the above class in this way:

@RunWith(JUnitParamsRunner::class)
class EmailIdValidatorTest {

    @Test
    @Parameters(value = [
        "[email protected], true",
        "[email protected], true",
        "[email protected], true",
        "mary@testdomaindotcom, false",
        "mary-smith@testdomain, false",
        "testdomain.com, false"
    ])
    fun testIsValidEmailId(email: String, expected: Boolean) {
        val actual = EmailIdUtility.isValid(email)
        assertEquals(expected, actual)
    }
}

which for me is a lot easier than the JUnit way.

For more examples on how to use JUnitParams you can check the link.

With JUnit 5 this is a lot easier but currently JUnit 5 is not supported for Android tests if you don't use Android-JUnit 5 too.

like image 109
MatPag Avatar answered Nov 15 '22 21:11

MatPag


I know you're using JUnit 4, but if you consider to update to JUnit 5, you can use @TestFactory annotation to execute parametrized unit test.

In your case that would result in a test class like this:

import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory

internal class EmailIdValidatorTest {

    @TestFactory
    fun `test email id validity`() =
        listOf(
            "[email protected]" to true,
            "[email protected]" to true,
            "[email protected]" to true,
            "mary@testdomaindotcom" to false,
            "mary-smith@testdomain" to false,
            "testdomain.com" to false
        ).map {
            dynamicTest("email ${it.first} should be ${if (it.second) "valid" else "not valid" }") {
                val actual = EmailIdUtility.isValid(it.first)
                assertEquals(expected, actual)
            }
        }

}
like image 29
noiaverbale Avatar answered Nov 15 '22 20:11

noiaverbale