Question
How can I declare that a Kotlin test is expected to throw an ArithmeticException, equivalent to this JUnit 4 Java test?
@Test(expected = ArithmeticException.class)
public void omg() {
int blackHole = 1 / 0;
}
I tried ArithmeticException and ArithmeticException.class in Kotlin, but neither syntax compiles.
Short Answer
You will learn how Kotlin refers to classes in annotations, how to test exceptions with JUnit 4, and the preferred assertion-based approach used by JUnit 5.
Concept
Kotlin and Java use different syntax when referring to a class.
In Java, ArithmeticException.class produces a Class<ArithmeticException> object. In Kotlin, the equivalent class reference is:
ArithmeticException::class
For a JUnit 4 test, the expected annotation argument expects a Kotlin class reference (KClass). Therefore, use ArithmeticException::class.
import org.junit.Test
class CalculatorTest {
@Test(expected = ArithmeticException::class)
fun omg() {
val denominator = 0
val blackHole = 1 / denominator
}
}
Testing exceptions matters because errors are part of an application's behavior. For example, code may reject invalid input, deny unauthorized access, or fail when required data is missing. A test should confirm that the correct failure occurs in those situations.
Mental Model
Think of a class reference as writing down the address of a building rather than creating a person who lives there.
ArithmeticException()creates an exception object: it is like creating a person.ArithmeticException::classrefers to the exception type itself: it is like writing the building's address.
JUnit's expected setting needs the type of exception that should occur, so it needs the address: ArithmeticException::class.
Syntax and Examples
With JUnit 4, declare the expected exception in the @Test annotation:
import org.junit.Test
class DivisionTest {
@Test(expected = ArithmeticException::class)
fun divisionByZeroThrows() {
val divisor = 0
val result = 10 / divisor
}
}
ArithmeticException::class means “the ArithmeticException class.”
Avoid using a constant expression such as 10 / 0 in an example. Kotlin may report division by zero during compilation instead of allowing the test to run. Putting zero in a variable makes the division occur at runtime.
JUnit 5 syntax
JUnit 5 commonly uses assertThrows. This makes it clear which exact statement is expected to fail.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class DivisionTest {
@Test
fun divisionByZeroThrows() {
val exception = assertThrows<ArithmeticException> {
val divisor = 0
10 / divisor
}
println(exception.message)
}
}
The generic type in assertThrows<ArithmeticException> specifies the expected exception type.
Step by Step Execution
Consider this JUnit 4 test:
@Test(expected = ArithmeticException::class)
fun divisionByZeroThrows() {
val divisor = 0
val result = 10 / divisor
}
Execution proceeds as follows:
- JUnit discovers the function because it has the
@Testannotation. - JUnit reads
expected = ArithmeticException::classand records that this test should throw anArithmeticException. - The test function starts and stores
0indivisor. - Kotlin evaluates
10 / divisoron the JVM. - Integer division by zero throws
ArithmeticException. - JUnit sees the expected exception and marks the test as passed.
If the division did not throw an exception, JUnit would fail the test because an exception was expected.
Real World Use Cases
Exception tests are useful whenever failure is an intentional part of the contract.
- Input validation: reject a negative quantity or malformed value.
- Authentication: throw an access-related exception when a user is not authorized.
- Data lookup: signal that a requested record does not exist.
- File processing: verify behavior when a required file cannot be read.
- Parsing: ensure invalid JSON, dates, or identifiers produce a meaningful failure.
For example, a money transfer service might reject a non-positive amount:
fun transfer(amount: Int) {
require(amount > 0) { "Amount must be positive" }
}
A test can verify that invalid input throws IllegalArgumentException.
Real Codebase Usage
In production codebases, developers usually test both the exception type and useful details such as its message. JUnit 5's assertThrows is often preferred because the risky code is wrapped in a small block.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
class TransferServiceTest {
@Test
fun rejectsNonPositiveAmount() {
val exception = assertThrows<IllegalArgumentException> {
transfer(0)
}
assertEquals("Amount must be positive", exception.message)
}
}
This style prevents a test from passing because of an exception thrown earlier than intended. It also supports a common validation pattern: fail early with require, check, or a domain-specific exception.
Use a domain-specific exception when callers need to distinguish one business failure from another:
class InsufficientFundsException(message: String) : RuntimeException(message)
Common Mistakes
Using Java's .class syntax
This is Java syntax and is invalid in Kotlin:
// Does not compile
@Test(expected = ArithmeticException.class)
fun test() {}
Use ::class instead:
@Test(expected = ArithmeticException::class)
fun test() {}
Forgetting the ::class reference
This looks like a class is being used as a value, which Kotlin does not allow:
// Does not compile
@Test(expected = ArithmeticException)
fun test() {}
Use ArithmeticException::class.
Creating an exception instead of referring to its type
// Not appropriate for JUnit 4's expected property
@Test(expected = ArithmeticException())
fun test() {}
ArithmeticException() creates an object. The annotation requires a class reference.
Letting the wrong line throw
With JUnit 4's expected, any matching exception anywhere in the test makes it pass:
@Test(expected = IllegalArgumentException::class)
fun test() {
require(false) // The test already passes here.
codeThatWasSupposedToThrow()
}
Prefer JUnit 5 assertThrows when you need to identify the exact operation that must fail.
Mixing JUnit 4 and JUnit 5 imports
JUnit 4 uses:
import org.junit.Test
JUnit 5 uses:
import org.junit.jupiter.api.Test
Ensure your test dependency and imports use the same JUnit version.
Comparisons
| Approach | Best use | Can inspect exception? | Important behavior |
|---|---|---|---|
JUnit 4 @Test(expected = ...) | Simple legacy JUnit 4 tests | No | Any matching exception in the whole test passes it |
JUnit 5 assertThrows<T> { ... } | New JUnit 5 tests | Yes | Only the code inside the block is checked |
try / catch | Rare custom assertions | Yes | More verbose and easier to write incorrectly |
Class reference versus object creation
| Kotlin expression | Meaning |
|---|---|
ArithmeticException::class | A reference to the exception class |
ArithmeticException() | A newly created exception object |
ArithmeticException::class.java | The underlying Java Class object, needed by some Java APIs |
For JUnit 4 Kotlin annotations, use ArithmeticException::class.
Cheat Sheet
// JUnit 4: expected exception in an annotation
import org.junit.Test
@Test(expected = IllegalArgumentException::class)
fun rejectsInvalidInput() {
require(false)
}
// JUnit 5: preferred focused assertion
import org.junit.jupiter.api.assertThrows
val exception = assertThrows<IllegalArgumentException> {
require(false) { "Invalid input" }
}
- Kotlin class reference:
TypeName::class - Java class reference:
TypeName.class - Create an object:
TypeName() - Use
assertThrowswhen you need to inspect the message or isolate the failing statement. - JUnit 4
expectedaccepts the declared exception type and its subclasses.
FAQ
What is the Kotlin equivalent of Exception.class?
Use Exception::class.
How do I expect an exception in a Kotlin JUnit 4 test?
Use @Test(expected = MyException::class) with import org.junit.Test.
Why does ArithmeticException.class not compile in Kotlin?
.class is Java syntax. Kotlin uses ::class for a class reference.
Why does Kotlin say that ArithmeticException has no companion object?
Without ::class or (), Kotlin interprets the class name as though you are trying to access a value on the class. A class itself is not a value in that position.
Should I use JUnit 4 expected or JUnit 5 assertThrows?
For new JUnit 5 tests, prefer assertThrows. It scopes the expected failure to a specific block and returns the exception for further assertions.
Can I test an exception message too?
Yes. Use JUnit 5 assertThrows to capture the exception, then assert its message.
Does a JUnit 4 expected exception test pass for subclasses?
Yes. A test expecting a base exception type also passes when code throws a subtype of that exception.
Mini Project
Description
Create a small Kotlin validator for account registration data. The validator should reject invalid values with meaningful exceptions, and the tests should verify both the exception type and message. This mirrors validation used in API endpoints and service-layer code.
Goal
Write exception tests for invalid registration input using JUnit 5 assertThrows.
Requirements
- Create a
registerfunction that accepts an email and an age. - Reject a blank email with
IllegalArgumentException. - Reject an age below 18 with
IllegalArgumentException. - Return a success message for valid input.
- Test both invalid cases and assert their exception messages.
- Test one valid registration case.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.