How to Implicitly Infer the Type of Fail Method in JUnit 5

David Mbochi Njonge Feb 02, 2024
  1. Create a Kotlin Project and Add Dependencies
  2. Not enough information to infer type variable V
  3. Infer the Type of the fail() Method Explicitly
  4. Use the fail() Method With Lambda Expressions
  5. Implicitly Inferring the Type of the fail() Method
  6. Conclusion
How to Implicitly Infer the Type of Fail Method in JUnit 5

Testing is an important step in the development of any application as it helps detect bugs early in the development stage, improves the application’s performance, and reduces the cost of development.

Suppose testing is a critical process in an application. In that case, it’s recommended to use the test-driven development (TDD) approach, which starts implementing any feature with failing tests, followed by the actual code that finally makes the test pass.

Different types of testing can be done in an application, including unit testing, integration testing, functional testing, and others. This tutorial will teach how to implicitly infer the type of the fail() method used in the unit testing stage with JUnit 5.

Create a Kotlin Project and Add Dependencies

Open IntelliJ development environment and select File > New > Project. On the window that opens, enter the project Name as kotlin-testing, select Kotlin on the Language section, and select Gradle on the Build system section.

Press the Create button to generate the project.

Open the build.gradle file and ensure you have the junit-jupiter-api dependency as shown below. This dependency provides us with the APIs we use to test our code.

dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
    testImplementation 'org.jetbrains.kotlin:kotlin-test'
}

Create a Main.kt file under the src/main/kotlin folder and copy and paste the following code into the file.

class Student(
    var id: Int,
    var studentName: String?,
    var studentIDCard: String?){

    override fun toString(): String {
        return "student name: $studentName, ID card: $studentIDCard"
    }
}

fun getStudents(): List<Student>{
    return listOf(
        Student(1,"john doe","CSO12022"),
        Student(2,"mary public","CS022022"),
        Student(3,"elon mask","S032022")
    );
}

In the above code, we have created a list that contains student objects, and we will use this list to do the testing.

Not enough information to infer type variable V

Create a Main.kt file under the src/test/kotlin folder and copy and paste the following code into the file.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.fail

class TestStudents{
    private val students: List<Student> = getStudents();

     @Test
    fun checkEmptyClass(){
        if (students.isNotEmpty()){
            fail("The class is not empty")
        }
    }

}

In this code, we have created a test with the name checkEmptyClass() that uses the list we created in the previous section to check whether it is empty. If the list is not empty, we call the fail() method and pass an error message as the method’s argument.

Note that we use the static method fail() of the Assertions class. The fully qualified name of the class is org.junit.jupiter.api.Assertions.

When using the fail() method from this class, the compiler shows a warning with the message Not enough information to infer type variable V because the method is generic and we have not provided any type parameters.

Infer the Type of the fail() Method Explicitly

The easiest approach that first comes to mind is explicitly providing the type parameters to appease the compiler, as shown in the following code.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.fail

class TestStudents{
    private val students: List<Student> = getStudents();

      @Test
    fun checkEmptyClass(){
        if (students.isNotEmpty()){
            fail<String>("The class is not empty")
        }
    }

}

Note that the reason we used the statement to appease the compiler is that the type is never returned because the exception org.opentest4j.AssertionFailedError is thrown before reaching the return statement.

In simple terms, we provide a generic parameter that is useless in our code. The next section shows how we can call this method without explicitly providing the generic parameters.

Run this test and ensure it fails with the following message.

The class is not empty
org.opentest4j.AssertionFailedError: The class is not empty

Use the fail() Method With Lambda Expressions

Comment on the example above and copy and paste the following code into the Main.kt file under the src/test/kotlin folder.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.fail

class TestStudents{
    private val students: List<Student> = getStudents();

    @Test
    fun findInvalidStudentIDCard(){
       students.forEach { student: Student ->
           if (student.studentIDCard?.startsWith("CS") == true){
               println(student)
           }else{
               fail("$student has an invalid id");
           }
       }
    }

}

In this example, we have created a test named findInvalidStudentIDCard() that iterates through the student list, logs the students with a valid card number, and calls the fail() method if the card number is invalid.

We have used the forEach() method that accepts one parameter and does not return any value. This is usually referred to as a Consumer.

When working with lambda expressions, we do not need to infer the type explicitly, as the compiler can infer the type from the Consumer passed to the method.

In this code, the compiler does not show any compile time errors. Run this test and ensure the output is as shown below.

student name: john doe, ID card: CSO12022
student name: mary public, ID card: CS022022

student name: elon mask, ID card: S032022 has an invalid id
org.opentest4j.AssertionFailedError: student name: elon mask, ID card: S032022 has an invalid id

Implicitly Inferring the Type of the fail() Method

Comment on the example above and copy and paste the following code into the Main.kt file under the src/test/kotlin folder.

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.fail

class TestStudents{
    private val students: List<Student> = getStudents();

    @Test
    fun checkClassSize(){
        if (students.count() < 5){
            fail("The class is not full")
        }
    }

}

In this example, we have created a test named checkClassSize() that counts the number of student objects in the list, and if the count is less than 5, we call the fail() method and pass an error message as the argument of the method.

Note that the fail() method in this example is not the generic method from the Assertions class. The fail() method in this example comes from the org.junit.jupiter.api package, which is not generic.

Since the method is not generic, we do not need to pass any parameters. Run this test and ensure the output is as shown below.

The class is not full
org.opentest4j.AssertionFailedError: The class is not full

Conclusion

In this tutorial, we have learned what causes the compile time error when using the fail() method. We have learned how we can explicitly infer the type of the method to avoid this warning.

In the last two sections, we have learned how the type is implicitly inferred when using lambda expressions and how we avoid inferring the type by using a method that is not generic.

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub