smali

пятница, 3 августа 2018 г.

can! → Parameterized JUnit4 Unit Tests in Kotlin


http://rossharper.net/2016/02/parameterized-junit4-unit-tests-in-kotlin/

This weekend I’ve been learning a little Kotlin — the (relatively) new JVM language from JetBrains. Once I’d worked my through the “Koans”, I decided to try a simple Code Kata. I chose the Roman Numerals Kata for this exercise. Whilst doing this, I wanted to use a parameterised unit test, and had to figure out how to do that in Kotlin… I wont go into the details of the Kata (read the link if you are interested), but in a nutshell, the goal is to write a program that will convert the Arabic numerals we are accustomed to in the modern world to the representation used by the ancient Romans. For example, you may have noticed Roman Numerals used to denote the year a movie was released: 1977 becomes “MCMLXXVII”.


As I set out on the exercise using TDD, I got to the point where I had two similar unit tests:
class RomanNumeralGeneratorTest {
private var generator: RomanNumeralGenerator = RomanNumeralGenerator()
@Test
fun shouldReturnIfor1() {
assertThat(generator.arabicToRoman(1), equalTo("I"));
}
@Test
fun shouldReturnIIfor2() {
assertThat(generator.arabicToRoman(2), equalTo("II"));
}
}
At this point we can see that there is some duplication in the tests. The rest of the tests will follow a similar pattern. Therefore, I wanted to refactor the tests to become parameterised tests — this is what I’d usually do when writing a Java JUnit4 test, after all. In Java, this involves:
  • indicating that the Parameterized test runner should be used
  • adding a static method — denoted by the @Parameters annotation — that that will provide the collection of test cases containing an array of parameters for each test
  • add a constructor to the test class that will receive the parameters for each test case
It may look a little like this:
@RunWith(Parameterized.class)
public class JavaTest {
private final int paramOne;
private final String paramTwo;
@Parameterized.Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[][] {
{1, "I"}, // First test: (paramOne = 1, paramTwo = "I")
{1999, "MCMXCIX"} // Second test: (paramOne = 1999, paramTwo = "MCMXCIX")
});
}
public JavaTest(int paramOne, String paramTwo) {
this.paramOne = paramOne;
this.paramTwo = paramTwo;
}
@Test
public void shouldReturnExpectedRomanForArabic() {
assertThat(new RomanNumeralGenerator().arabicToRoman(paramOne), equalTo(paramTwo));
}
}
(obviously, we’d use better variable names than paramOne and paramTwo when doing this for real, I’ve used these here for clarity)
Creating a parameterised test in Kotlin is a little different than in Java. The main reason being that Kotlin does not have static member functions on classes. However, we can add compatibility for calling Java code, by putting our function in a “companion object”, and annotating it with the @JvmStatic annotation (Kotlin Reference).
For example:
class C {
companion object {
@JvmStatic
fun foo() {
}
}
}
Therefore, we can create a parameterised Kotlin unit test like so:
@RunWith(Parameterized::class)
class KotlinTest(val paramOne: Int, val paramTwo: String) {
companion object {
@JvmStatic
@Parameterized.Parameters
fun data() : Collection<Array<Any>> {
return listOf(
arrayOf(1, "I"), // First test: (paramOne = 1, paramTwo = "I")
arrayOf(1999, "MCMXCIX") // Second test: (paramOne = 1999, paramTwo = "MCMXCIX")
)
}
}
@Test
fun shouldReturnExpectedRomanForArabic() {
assertThat(RomanNumeralGenerator().arabicToRoman(paramOne), equalTo(paramTwo));
}
}
If you are interested in seeing this in action, you can check out my completed Roman Numerals Kata in Kotlin on Github: https://github.com/rossharper/RomanNumerals-Kotlin
git clone https://github.com/rossharper/RomanNumerals-Kotlin.git
 

TL;DR

To create a parameterised unit test in Kotlin:
  • annotate the Kotlin test class with @RunWith(Parameterized::class)
  • place the @Parameterized.Parameters function within a companion object in the test class
  • annotate the @Parameterized.@Parameters function with the @JvmStaticannotation — as Kotlin doesn’t have static functions, this allows Java code to call this Kotlin as if it were a static
  • Use Kotlin’s Any in place of Java’s Object  

Комментариев нет:

Отправить комментарий