Kotlin Parameterized Test: What is it?

Photo of author

Development teams use functions and classes to produce software code that is both reusable and loosely linked. The parameters given to the code have an impact on how it behaves. The newest iteration of the renowned Java testing framework is JUnit 5, which was just published. This version, primarily based on lambda expressions, adds various features designed to target capabilities made available in Kotlin.

We will discuss many arguments in a Kotlin parameterized test, the kinds of tests in Kotlin, what precisely a Kotlin parameterized test is, and other commonly asked topics in this article.

The Kotlin parameterized test addresses the most frequent issues while providing a structure for testing existing or new functions. Now, let’s concentrate on JUnit 5’s parameterized tests.

Visit: Kotlin

Check this out: Kotlin Tutorial: Add New Element To Array

Kotlin Parameterized Test: Overview

A JUnit 5 test created in Kotlin performs well at the most basic level. We make a test class, add the @Test annotation to our test methods, write the code, and execute the assertions.


class CalculatorTest {

    private val calculator = Calculator()

    @Test

    fun whenAdding1and3_thenAnswerIs4() {

        Assertions.assertEquals(4, calculator.add(1, 3))

    }

}

Here, everything usually functions. As in Java, we can engage with attributes in the test class.

Remember that separate imports are needed, and we do assertions using the Assertions class rather than the Assert class. This modification is common to JUnit 5 and is not unique to Kotlin.

Data-Driven Tests

The native support for data-driven tests in JUnit 5 is one of the significant upgrades. The usage of functional mappings on collections helps make our tests simpler to comprehend and manage, and these also operate as well in Kotlin.

Tagged Tests

Since version 1.6, the Kotlin programming language supports repeating annotations on classes and functions. Using tags becomes less verbose because we do not need to encapsulate tags in the @Tags annotation. But we must designate @Tag as @java.lang.annotation.

Repeatable in the bytecode so that it may also be repeated in Java:


// JVM bytecode: @Tag.Container(value = {@Tag("slow"), @Tag("logarithms")})

@Tag("slow") @Tag("logarithms")

@Test

fun `Repeatable Tag Log to base 2 of 8 should be equal to 3`() {

    Assertions.assertEquals(3.0, calculator.log(2, 8))

}

Kotlin Parameterized Tests

JUnit 5 has experimental enhancements that make it simpler to build parameterized tests. The @ParameterizedTest annotation from the dependency org.junit.jupiter:junit-jupiter-params is used for these:

</pre>
<dependency>

<groupId>org.junit.jupiter</groupId>

<artifactId>junit-jupiter-params</artifactId>

<version>5.0.0</version>

</dependency>
<pre>

On Maven Central, you can find the most recent version.

JUnit Parameterized Tests: Multiple arguments

JUnit5 provides several source labels. Some of these annotations are briefly described and given as examples in the following sections.

JUnit Parameterized Tests

See also: Java Automation Framework: Streamlining Testing And Deployment

MethodSource

This annotation enables us to load data from any number of test class factory functions and outside classes. Each factory function must produce a continuous sequence of arguments.

Explicit Method Source: The test will attempt to load the specified method.

E.g.

</pre>
@ParameterizedTest

@MethodSource("checkExplicitMethodSourceArgs")

void checkExplicitMethodSource(String word) {
<pre></pre>
assertTrue(StringUtils.isAlphanumeric(word),
<pre></pre>
"Supplied word is not alpha-numeric");
<pre></pre>
}
<pre></pre>
static Stream<String> checkExplicitMethodSourceArgs() {
<pre></pre>
return Stream.of("a1",  "b2");
<pre></pre>
}
<pre>

Source of the implicit method: The test will look for the source function whose name corresponds to the sample-case method.

E.g.

</pre>
@ParameterizedTest

@MethodSource

void checkImplicitMethodSource(String word) {

assertTrue(StringUtils.isAlphanumeric(word),

"Supplied word is not alpha-numeric");

}
<pre></pre>
static Stream<String> checkImplicitMethodSource() {

return Stream.of("a1",

"b2");

}
<pre>

Source for a multi-argument method: We must provide the arguments as streams of data. Following the index, the test will predict the parameters.

E.g.

</pre>
@ParameterizedTest

@MethodSource

void checkMultiArgumentsMethodSource(int number, String expected) {

assertEquals(StringUtils.equals(expected, "even") ? 0 : 1, number % 2);

}
<pre></pre>
static Stream<Arguments> checkMultiArgumentsMethodSource() {

return Stream.of(Arguments.of(2, "even"),

Arguments.of(3, "odd"));

}
<pre>

Source of the external method: This test will attempt to load the foreign process.

E.g.


@ParameterizedTest

@MethodSource(

"source.method.ExternalMethodSource#checkExternalMethodSourceArgs")

void checkExternalMethodSource(String word) {

assertTrue(StringUtils.isAlphanumeric(word), 

               "Supplied word is not alpha-numeric");

}

ValueSource

It is among the accessible sources. It accepts one literal array of values. There are several literal values that ValueSource supports, including short, byte, int, long, float, double, char, boolean, String, and Class.

E.g.


@ParameterizedTest

@ValueSource(strings = { "a1", "b2" })

void checkAlphanumeric(String word) {

assertTrue(StringUtils.isAlphanumeric(word), 

"Supplied word is not alpha-numeric");

}

CsvSource

As comma-separated values (CSV String literals), this annotation will enable us to send parameter lists. The parameterized test is run once for each entry in the CSV file. Use of the property useHeadersInDisplayName also makes it possible to exclude the CSV header.

E.g.


@ParameterizedTest

@CsvSource({ "2, even", 

             "3, odd"})

void checkCsvSource(int number, String expected) {

assertEquals(StringUtils.equals(expected, "even") 

? 0 : 1, number % 2);

}

EnumSource

Enum constants may be easily used as test-case parameters thanks to this annotation. Supported attributes include

Value: An enum class type; for illustration, use ChronoUnit.class

E.g.


package java.time.temporal;

public enum ChronoUnit implements TemporalUnit {

SECONDS("Seconds", Duration.ofSeconds(1)),

MINUTES("Minutes", Duration.ofSeconds(60)),

    HOURS("Hours", Duration.ofSeconds(3600)),

DAYS("Days", Duration.ofSeconds(86400)),

//12 other units

}

ChronoUnit: Standard date and time units are contained in the ChronoUnit enum.

E.g.


@ParameterizedTest

@EnumSource(ChronoUnit.class)

checkEnumSourceValue(ChronoUnit unit) is a void function.

   assertNotNull(unit);s                                                                                                                                                                                                                                                                                                                                                             

}

In this case, @EnumSource will supply all 16 ChronoUnit enums as arguments.

names – The names of the enum elements to supply, or a regular expression to choose the names, for instance, DAYS or.*DAYS$

E.g.


@ParameterizedTest

@EnumSource(names = { "DAYS", "HOURS" })

void checkEnumSourceNames(ChronoUnit unit) {

assertNotNull(unit);

}

NullSource & EmptySource

Consider checking the user’s input for the username and password in a login function to see whether they have completed all the required fields. Using the annotations, we verify that the given fields are neither null, empty, or blank.

We may give the source script with null, empty, and blank values and check the operation of your source code by using the unit tests’ @NullSource and @EmptySource functions.

E.g.


@ParameterizedTest

@NullSource

void checkNull(String value) {

assertEquals(null, value);

}

@ParameterizedTest

@EmptySource

void checkEmpty(String value) {

assertEquals("", value);

}

Combining the @NullAndEmptySource and @ValueSource(strings = “” and “””) is another method for passing null, empty, and blank input values. This method covers all potential harmful outcomes.

E.g.


@ParameterizedTest

@NullAndEmptySource

@ValueSource(strings = { " ", " " })

void checkNullEmptyAndBlank(String value) {

assertTrue(value == null || value.isBlank());

}

ArgumentsSource

This annotation offers a unique, reusable ArgumentsProvider. ArgumentsProvider must be implemented by a static or external nested class.

E.g.

</pre>
@ParameterizedTest

@ArgumentsSource(ExternalArgumentsProvider.class)

void checkExternalArgumentsSource(int number, String expected) {

assertEquals(StringUtils.equals(expected, "even")

? 0 : 1, number % 2,

"Supplied number " + number +

" is not an " + expected + " number");

}

}
<pre></pre>
public class ExternalArgumentsProvider implements ArgumentsProvider {
<pre></pre>
@Override

public Stream<? extends Arguments> provideArguments(

ExtensionContext context) throws Exception {
<pre></pre>
return Stream.of(Arguments.of(2, "even"),

Arguments.of(3, "odd"));

}

}

FAQs

Besides JUnit 5, can I utilize parameterized tests with Kotlin's testing tools like Spek or Kotest?

You may utilize parameterized tests using Kotlin's Spek or Kotest testing frameworks. For detailed information, you should refer to the documentation for these frameworks because they could each have their methods for constructing parameterized tests.

What do Kotlin parameterized tests do?

The same test methodology may be executed using parameterized tests with various input data sets. It enables you to test code against different inputs and anticipated results.

What are the advantages of parameterized testing?

Because the same test logic is used for several input situations in parameterized tests, you may write test code that is shorter and easier to maintain. This minimizes code duplication and facilitates the future addition of more test cases.

Conclusion

The Kotlin parameterized test feature of JUnit5 makes testing more effective. This is done by removing the need for duplicate test cases and enabling the option to execute the same test several times with different inputs. This not only helps the development team save time and effort, but it also broadens the scope and improves the efficiency of the testing procedure.

Additionally, because the source code may be checked using a more extensive variety of inputs, this feature makes it possible to test it more thoroughly. It increases the likelihood that any bugs or other problems will be found. In general, parameterized tests in JUnit5 are a valuable tool for enhancing the quality and dependability of the code.

Read also: Kotlin Array Length: Everything To Know

Leave a Comment