Skip to content

Commit

Permalink
Widen isInstanceOf/isNotInstanceOf assert to operate on nullable types
Browse files Browse the repository at this point in the history
This behavior brings it more line with how 'is' behaves in the language.
  • Loading branch information
JakeWharton authored and evant committed Oct 29, 2024
1 parent 3ef715c commit 89dcda1
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- renamed `prop` to `having` as part of effort to unify API naming, old name is deprecated.
- renamed `suspendCall` to `having` as part of effort to unify API naming, old name is deprecated.
- added `doesNotExist` assertions to `Path`.
- the receiver types for `isTrue()`, `isFalse()`, and `isInstanceOf()` have been widened to operate on nullable values.

## [0.28.1] 2024-04-17

Expand Down
11 changes: 6 additions & 5 deletions assertk/src/commonMain/kotlin/assertk/assertions/any.kt
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,15 @@ fun <T : Any> Assert<T>.doesNotHaveClass(kclass: KClass<out T>) = given { actual
* @see [isInstanceOf]
* @see [doesNotHaveClass]
*/
inline fun <reified T : Any> Assert<Any>.isNotInstanceOf() = isNotInstanceOf(T::class)
inline fun <reified T : Any> Assert<Any?>.isNotInstanceOf() = isNotInstanceOf(T::class)

/**
* Asserts the value is not an instance of the expected kotlin class. Both
* `assertThat("test").isNotInstanceOf(String::class)` and `assertThat("test").isNotInstanceOf(Any::class)` fail.
* @see [isInstanceOf]
* @see [doesNotHaveClass]
*/
fun <T : Any> Assert<T>.isNotInstanceOf(kclass: KClass<out T>) = given { actual ->
fun <T : Any> Assert<T?>.isNotInstanceOf(kclass: KClass<out T>) = given { actual ->
if (!kclass.isInstance(actual)) return
expected("to not be instance of:${show(kclass)}")
}
Expand All @@ -270,20 +270,21 @@ fun <T : Any> Assert<T>.isNotInstanceOf(kclass: KClass<out T>) = given { actual
* @see [isNotInstanceOf]
* @see [hasClass]
*/
inline fun <reified T : Any> Assert<Any>.isInstanceOf() = isInstanceOf(T::class)
inline fun <reified T : Any> Assert<Any?>.isInstanceOf() = isInstanceOf(T::class)

/**
* Asserts the value is an instance of the expected kotlin class. Both `assertThat("test").isInstanceOf(String::class)` and
* `assertThat("test").isInstanceOf(Any::class)` are successful.
* @see [isNotInstanceOf]
* @see [hasClass]
*/
fun <T : Any> Assert<Any>.isInstanceOf(kclass: KClass<T>): Assert<T> = transform(name) { actual ->
fun <T : Any> Assert<Any?>.isInstanceOf(kclass: KClass<T>): Assert<T> = transform(name) { actual ->
if (kclass.isInstance(actual)) {
@Suppress("UNCHECKED_CAST")
actual as T
} else {
expected("to be instance of:${show(kclass)} but had class:${show(actual::class)}")
val but = if (actual == null) "was null" else "had class:${show(actual::class)}"
expected("to be instance of:${show(kclass)} but $but")
}
}

Expand Down
32 changes: 32 additions & 0 deletions assertk/src/commonTest/kotlin/test/assertk/assertions/AnyTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,28 @@ class AnyTest {
)
}

@Test
fun isInstanceOf_kclass_null_fails() {
val error = assertFailsWith<AssertionError> {
assertThat(null as DifferentObject?).isInstanceOf(DifferentObject::class)
}
assertEquals(
"expected to be instance of:<${DifferentObject::class}> but was null",
error.message
)
}

@Test
fun isInstanceOf_reified_kclass_null_fails() {
val error = assertFailsWith<AssertionError> {
assertThat(null as DifferentObject?).isInstanceOf<DifferentObject>()
}
assertEquals(
"expected to be instance of:<${DifferentObject::class}> but was null",
error.message
)
}

@Test
fun isInstanceOf_kclass_run_block_when_passes() {
val error = assertFailsWith<AssertionError> {
Expand Down Expand Up @@ -503,6 +525,16 @@ class AnyTest {
assertThat(subject).isNotInstanceOf<DifferentObject>()
}

@Test
fun isNotInstanceOf_kclass_null_passes() {
assertThat(null as DifferentObject?).isNotInstanceOf(DifferentObject::class)
}

@Test
fun isNotInstanceOf_reified_kclass_null_passes() {
assertThat(null as DifferentObject?).isNotInstanceOf<DifferentObject>()
}

@Test
fun isNotInstanceOf_kclass_same_class_fails() {
val error = assertFailsWith<AssertionError> {
Expand Down
9 changes: 5 additions & 4 deletions assertk/src/jvmMain/kotlin/assertk/assertions/any.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ fun <T : Any> Assert<T>.doesNotHaveClass(jclass: Class<out T>) = given { actual
* @see [isNotInstanceOf]
* @see [hasClass]
*/
fun <T : Any, S : T> Assert<T>.isInstanceOf(jclass: Class<S>): Assert<S> = transform { actual ->
fun <T : Any> Assert<Any?>.isInstanceOf(jclass: Class<T>): Assert<T> = transform { actual ->
if (jclass.isInstance(actual)) {
@Suppress("UNCHECKED_CAST")
actual as S
actual as T
} else {
expected("to be instance of:${show(jclass)} but had class:${show(actual.javaClass)}")
val but = if (actual == null) "was null" else "had class:${show(actual.javaClass)}"
expected("to be instance of:${show(jclass)} but $but")
}
}

Expand All @@ -61,7 +62,7 @@ fun <T : Any, S : T> Assert<T>.isInstanceOf(jclass: Class<S>): Assert<S> = trans
* @see [isInstanceOf]
* @see [doesNotHaveClass]
*/
fun <T : Any> Assert<T>.isNotInstanceOf(jclass: Class<out T>) = given { actual ->
fun <T : Any> Assert<T?>.isNotInstanceOf(jclass: Class<out T>) = given { actual ->
if (!jclass.isInstance(actual)) return
expected("to not be instance of:${show(jclass)}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package test.assertk.assertions
import assertk.assertThat
import assertk.assertions.*
import test.assertk.opentestPackageName
import java.lang.Exception
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
Expand All @@ -30,6 +29,17 @@ class JavaAnyTest {
assertThat(subject).isInstanceOf(TestObject::class.java)
}

@Test
fun isInstanceOf_jclass_null_fails() {
val error = assertFailsWith<AssertionError> {
assertThat(null as BasicObject?).isInstanceOf(BasicObject::class.java)
}
assertEquals(
"expected to be instance of:<$p\$BasicObject> but was null",
error.message,
)
}

@Test
fun isInstanceOf_jclass_different_class_fails() {
val error = assertFailsWith<AssertionError> {
Expand Down Expand Up @@ -72,6 +82,11 @@ class JavaAnyTest {
assertThat(subject).isNotInstanceOf(DifferentObject::class.java)
}

@Test
fun isNotInstanceOf_jclass_null_passess() {
assertThat(null as DifferentObject?).isNotInstanceOf(DifferentObject::class.java)
}

@Test
fun isNotInstanceOf_jclass_same_class_fails() {
val error = assertFailsWith<AssertionError> {
Expand Down

0 comments on commit 89dcda1

Please sign in to comment.