Core assertion and matcher library for Kotest testing framework
—
JVM reflection-based matchers for classes, annotations, properties, and method validation using Kotlin reflection API for comprehensive type and metadata assertion capabilities.
Matchers for validating class-level annotations and annotation metadata.
/**
* Assert that class has at least one annotation
* @return The original KClass for chaining
*/
fun KClass<*>.shouldHaveAnnotations(): KClass<*>
/**
* Assert that class has no annotations
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotHaveAnnotations(): KClass<*>
/**
* Assert that class has exactly specified number of annotations
* @param count Expected number of annotations
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveAnnotations(count: Int): KClass<*>
/**
* Assert that class does not have specified number of annotations
* @param count Annotation count that should not match
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldNotHaveAnnotations(count: Int): KClass<*>
/**
* Assert that class is annotated with specific annotation type
* @param T The annotation type to check for
* @param block Optional validation block for annotation properties
* @return The original KClass for chaining
*/
inline fun <reified T : Annotation> KClass<*>.shouldBeAnnotatedWith(
noinline block: (T) -> Unit = {}
): KClass<*>
/**
* Assert that class is not annotated with specific annotation type
* @param T The annotation type that should not be present
* @return The original KClass for chaining
*/
inline fun <reified T : Annotation> KClass<*>.shouldNotBeAnnotatedWith(): KClass<*>
/**
* Create matcher for annotation count validation
* @param count Expected number of annotations (-1 for any positive count)
* @return Matcher that passes when class has expected annotation count
*/
fun haveClassAnnotations(count: Int = -1): Matcher<KClass<*>>
/**
* Create matcher for specific annotation presence
* @param T The annotation type to check for
* @param block Optional validation block for annotation properties
* @return Matcher that passes when class has the specified annotation
*/
inline fun <reified T : Annotation> beAnnotatedWith(
noinline block: (T) -> Unit = {}
): Matcher<KClass<*>>Usage Examples:
import io.kotest.matchers.reflection.*
import kotlin.reflect.KClass
@Entity
@Table(name = "users")
@Serializable
data class User(
@Id val id: Long,
@Column(name = "username") val name: String
)
val userClass: KClass<User> = User::class
// Annotation presence validation
userClass.shouldHaveAnnotations()
userClass.shouldHaveAnnotations(3) // Entity, Table, Serializable
// Specific annotation validation
userClass.shouldBeAnnotatedWith<Entity>()
userClass.shouldBeAnnotatedWith<Table> { table ->
table.name shouldBe "users"
}
// Using matcher syntax
userClass should haveClassAnnotations(3)
userClass should beAnnotatedWith<Serializable>()Matchers for validating class properties, fields, and their characteristics.
/**
* Assert that class has property with specified name
* @param name The property name to check for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveProperty(name: String): KClass<*>
/**
* Assert that class does not have property with specified name
* @param name The property name that should not exist
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldNotHaveProperty(name: String): KClass<*>
/**
* Assert that class has exactly specified number of properties
* @param count Expected number of properties
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHavePropertyCount(count: Int): KClass<*>
/**
* Assert that class has mutable property with specified name
* @param name The mutable property name to check for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveMutableProperty(name: String): KClass<*>
/**
* Assert that class has immutable property with specified name
* @param name The immutable property name to check for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveImmutableProperty(name: String): KClass<*>
/**
* Assert that property has specific type
* @param name Property name
* @param type Expected property type
* @return The original KClass for chaining
*/
fun KClass<*>.shouldHavePropertyOfType(name: String, type: KClass<*>): KClass<*>
/**
* Create matcher for property existence validation
* @param name The property name to check for
* @return Matcher that passes when class has the named property
*/
fun haveProperty(name: String): Matcher<KClass<*>>
/**
* Create matcher for property count validation
* @param count Expected number of properties
* @return Matcher that passes when class has exact property count
*/
fun havePropertyCount(count: Int): Matcher<KClass<*>>
/**
* Create matcher for mutable property validation
* @param name The mutable property name
* @return Matcher that passes when class has mutable property
*/
fun haveMutableProperty(name: String): Matcher<KClass<*>>
/**
* Create matcher for property type validation
* @param name Property name
* @param type Expected property type
* @return Matcher that passes when property has expected type
*/
fun havePropertyOfType(name: String, type: KClass<*>): Matcher<KClass<*>>Matchers for validating class constructors and member functions.
/**
* Assert that class has primary constructor with specified parameter count
* @param parameterCount Expected number of constructor parameters
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHavePrimaryConstructor(parameterCount: Int): KClass<*>
/**
* Assert that class does not have primary constructor
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotHavePrimaryConstructor(): KClass<*>
/**
* Assert that class has function with specified name
* @param name The function name to check for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveFunction(name: String): KClass<*>
/**
* Assert that class does not have function with specified name
* @param name The function name that should not exist
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldNotHaveFunction(name: String): KClass<*>
/**
* Assert that class has exactly specified number of member functions
* @param count Expected number of functions (excluding inherited)
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveFunctionCount(count: Int): KClass<*>
/**
* Assert that function has specific parameter types
* @param functionName Name of function to validate
* @param parameterTypes Expected parameter types in order
* @return The original KClass for chaining
*/
fun KClass<*>.shouldHaveFunctionWithParameters(
functionName: String,
vararg parameterTypes: KClass<*>
): KClass<*>
/**
* Create matcher for primary constructor validation
* @param parameterCount Expected parameter count
* @return Matcher that passes when class has primary constructor with parameter count
*/
fun havePrimaryConstructor(parameterCount: Int): Matcher<KClass<*>>
/**
* Create matcher for function existence validation
* @param name The function name to check for
* @return Matcher that passes when class has the named function
*/
fun haveFunction(name: String): Matcher<KClass<*>>
/**
* Create matcher for function count validation
* @param count Expected number of member functions
* @return Matcher that passes when class has exact function count
*/
fun haveFunctionCount(count: Int): Matcher<KClass<*>>
/**
* Create matcher for function signature validation
* @param functionName Name of function
* @param parameterTypes Expected parameter types
* @return Matcher that passes when function has expected signature
*/
fun haveFunctionWithParameters(
functionName: String,
vararg parameterTypes: KClass<*>
): Matcher<KClass<*>>Usage Examples:
import io.kotest.matchers.reflection.*
import kotlin.reflect.KClass
data class Person(
val id: Long,
var name: String,
val email: String
) {
fun updateName(newName: String) {
name = newName
}
fun isValid(): Boolean = name.isNotEmpty() && email.contains("@")
}
val personClass: KClass<Person> = Person::class
// Property validation
personClass shouldHaveProperty "id"
personClass shouldHaveProperty "name"
personClass shouldHavePropertyCount 3
personClass shouldHaveMutableProperty "name"
personClass shouldHaveImmutableProperty "id"
personClass.shouldHavePropertyOfType("id", Long::class)
// Constructor validation
personClass shouldHavePrimaryConstructor 3
// Function validation
personClass shouldHaveFunction "updateName"
personClass shouldHaveFunction "isValid"
personClass shouldHaveFunctionCount 2
personClass.shouldHaveFunctionWithParameters("updateName", String::class)Matchers for validating class types, inheritance relationships, and type characteristics.
/**
* Assert that class is abstract
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeAbstract(): KClass<*>
/**
* Assert that class is not abstract (concrete)
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeAbstract(): KClass<*>
/**
* Assert that class is a data class
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeDataClass(): KClass<*>
/**
* Assert that class is not a data class
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeDataClass(): KClass<*>
/**
* Assert that class is sealed
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeSealed(): KClass<*>
/**
* Assert that class is not sealed
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeSealed(): KClass<*>
/**
* Assert that class is an interface
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeInterface(): KClass<*>
/**
* Assert that class is not an interface
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeInterface(): KClass<*>
/**
* Assert that class is an enum class
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeEnum(): KClass<*>
/**
* Assert that class is not an enum class
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeEnum(): KClass<*>
/**
* Assert that class is subclass of specified parent class
* @param parent The parent class type
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldBeSubclassOf(parent: KClass<*>): KClass<*>
/**
* Assert that class is not subclass of specified class
* @param parent The class that should not be a parent
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldNotBeSubclassOf(parent: KClass<*>): KClass<*>
/**
* Create matcher for abstract class validation
* @return Matcher that passes for abstract classes
*/
fun beAbstract(): Matcher<KClass<*>>
/**
* Create matcher for data class validation
* @return Matcher that passes for data classes
*/
fun beDataClass(): Matcher<KClass<*>>
/**
* Create matcher for sealed class validation
* @return Matcher that passes for sealed classes
*/
fun beSealed(): Matcher<KClass<*>>
/**
* Create matcher for interface validation
* @return Matcher that passes for interfaces
*/
fun beInterface(): Matcher<KClass<*>>
/**
* Create matcher for enum validation
* @return Matcher that passes for enum classes
*/
fun beEnum(): Matcher<KClass<*>>
/**
* Create matcher for inheritance validation
* @param parent The parent class to check inheritance from
* @return Matcher that passes when class is subclass of parent
*/
fun beSubclassOf(parent: KClass<*>): Matcher<KClass<*>>Matchers for validating member visibility and modifier characteristics.
/**
* Assert that class has public member with specified name
* @param memberName Name of member to check visibility for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHavePublicMember(memberName: String): KClass<*>
/**
* Assert that class has private member with specified name
* @param memberName Name of member to check visibility for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHavePrivateMember(memberName: String): KClass<*>
/**
* Assert that class has protected member with specified name
* @param memberName Name of member to check visibility for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveProtectedMember(memberName: String): KClass<*>
/**
* Assert that class has internal member with specified name
* @param memberName Name of member to check visibility for
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveInternalMember(memberName: String): KClass<*>
/**
* Assert that function is suspend function
* @param functionName Name of function to check
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveSuspendFunction(functionName: String): KClass<*>
/**
* Assert that function is inline function
* @param functionName Name of function to check
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveInlineFunction(functionName: String): KClass<*>
/**
* Create matcher for public member validation
* @param memberName Name of member to check
* @return Matcher that passes when member is public
*/
fun havePublicMember(memberName: String): Matcher<KClass<*>>
/**
* Create matcher for private member validation
* @param memberName Name of member to check
* @return Matcher that passes when member is private
*/
fun havePrivateMember(memberName: String): Matcher<KClass<*>>
/**
* Create matcher for suspend function validation
* @param functionName Name of function to check
* @return Matcher that passes when function is suspend
*/
fun haveSuspendFunction(functionName: String): Matcher<KClass<*>>Usage Examples:
import io.kotest.matchers.reflection.*
abstract class Shape {
abstract fun area(): Double
}
data class Circle(val radius: Double) : Shape() {
override fun area(): Double = Math.PI * radius * radius
}
interface Drawable {
fun draw()
}
enum class Color { RED, GREEN, BLUE }
sealed class Result<out T> {
data class Success<T>(val value: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
}
// Class type validation
Shape::class.shouldBeAbstract()
Circle::class.shouldBeDataClass()
Circle::class shouldBeSubclassOf Shape::class
Drawable::class.shouldBeInterface()
Color::class.shouldBeEnum()
Result::class.shouldBeSealed()
// Using matcher syntax
Shape::class should beAbstract()
Circle::class should beDataClass()
Circle::class should beSubclassOf(Shape::class)
Drawable::class should beInterface()Matchers for validating generic types and type parameters.
/**
* Assert that class has specified number of type parameters
* @param count Expected number of type parameters
* @return The original KClass for chaining
*/
infix fun KClass<*>.shouldHaveTypeParameterCount(count: Int): KClass<*>
/**
* Assert that class does not have type parameters (non-generic)
* @return The original KClass for chaining
*/
fun KClass<*>.shouldNotBeGeneric(): KClass<*>
/**
* Assert that class is generic (has type parameters)
* @return The original KClass for chaining
*/
fun KClass<*>.shouldBeGeneric(): KClass<*>
/**
* Create matcher for type parameter count validation
* @param count Expected number of type parameters
* @return Matcher that passes when class has expected type parameter count
*/
fun haveTypeParameterCount(count: Int): Matcher<KClass<*>>
/**
* Create matcher for generic class validation
* @return Matcher that passes for generic classes
*/
fun beGeneric(): Matcher<KClass<*>>Usage Examples:
import io.kotest.matchers.reflection.*
class Container<T> {
var item: T? = null
}
class Repository<K, V> {
private val storage = mutableMapOf<K, V>()
}
class SimpleClass {
val value: String = ""
}
// Generic type validation
Container::class.shouldBeGeneric()
Container::class shouldHaveTypeParameterCount 1
Repository::class shouldHaveTypeParameterCount 2
SimpleClass::class.shouldNotBeGeneric()
// Using matcher syntax
Container::class should beGeneric()
Repository::class should haveTypeParameterCount(2)Reflection matchers provide detailed error information for assertion failures:
All reflection matchers handle edge cases like null types, missing members, and access restrictions while providing meaningful error messages that help debug reflection-based assertions.
Install with Tessl CLI
npx tessl i tessl/maven-io-kotest--kotest-assertions-core-jvm