Skip to content

Commit

Permalink
Allow override of assertion context's error message display.
Browse files Browse the repository at this point in the history
By default, contexts whose type is not known will fall back to its .toString(), which may not be desirable for some cases.
  • Loading branch information
Sean Amos committed Aug 21, 2020
1 parent 051cec0 commit a8c8b4b
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 8 deletions.
36 changes: 29 additions & 7 deletions assertk/src/commonMain/kotlin/assertk/assert.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package assertk

import assertk.assertions.isFailure
import assertk.assertions.isSuccess
import assertk.assertions.support.display
import assertk.assertions.support.show
import kotlin.contracts.contract
import kotlin.reflect.KProperty0

/**
Expand All @@ -17,7 +17,7 @@ annotation class AssertkDsl
* @see [assertThat]
*/
@AssertkDsl
sealed class Assert<out T>(val name: String?, internal val context: Any?) {
sealed class Assert<out T>(val name: String?, internal val context: AssertingContext) {
/**
* Transforms an assertion from one type to another. If the assertion is failing the resulting assertion will still
* be failing, otherwise the mapping function is called. An optional name can be provided, otherwise this
Expand Down Expand Up @@ -74,19 +74,30 @@ sealed class Assert<out T>(val name: String?, internal val context: Any?) {
}

@PublishedApi
internal class ValueAssert<out T>(val value: T, name: String?, context: Any?) :
internal class ValueAssert<out T>(val value: T, name: String?, context: AssertingContext) :
Assert<T>(name, context) {

override fun <R> assertThat(actual: R, name: String?): Assert<R> =
ValueAssert(actual, name, if (context != null || this.value === actual) context else this.value)
override fun <R> assertThat(actual: R, name: String?): Assert<R> {
val newContext = if (context.originatingSubject != null || this.value == actual) {
context
} else {
context.copy(originatingSubject = this.value)
}
return ValueAssert(actual, name, newContext)
}
}

@PublishedApi
internal class FailingAssert<out T>(val error: Throwable, name: String?, context: Any?) :
internal class FailingAssert<out T>(val error: Throwable, name: String?, context: AssertingContext) :
Assert<T>(name, context) {
override fun <R> assertThat(actual: R, name: String?): Assert<R> = FailingAssert(error, name, context)
}

internal data class AssertingContext(
val originatingSubject: Any?,
val displayOriginatingSubject: () -> String
)

/**
* Runs the given lambda if the block throws an error, otherwise fails.
*/
Expand Down Expand Up @@ -182,7 +193,18 @@ internal expect fun showError(e: Throwable): String
* assertThat(true, name = "true").isTrue()
* ```
*/
fun <T> assertThat(actual: T, name: String? = null): Assert<T> = ValueAssert(actual, name, null)
fun <T> assertThat(
actual: T,
name: String? = null,
displayActual: (T) -> String = { display(it) }
): Assert<T> = ValueAssert(
value = actual,
name = name,
context = AssertingContext(
originatingSubject = null,
displayOriginatingSubject = { displayActual(actual) }
)
)

/**
* Asserts on the given property reference using its name, if no explicit name is specified. This method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fun <T> Assert<T>.fail(expected: Any?, actual: Any?) {
*/
fun <T> Assert<T>.expected(message: String, expected: Any? = NONE, actual: Any? = NONE): Nothing {
val maybeSpace = if (message.startsWith(":")) "" else " "
val maybeInstance = if (context != null) " ${show(context, "()")}" else ""
val maybeInstance = if (context.originatingSubject != null) " (${context.displayOriginatingSubject()})" else ""
fail(
message = "expected${formatName(name)}$maybeSpace$message$maybeInstance",
expected = expected,
Expand Down
9 changes: 9 additions & 0 deletions assertk/src/commonTest/kotlin/test/assertk/AssertTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ class AssertTest {
assertEquals("expected [test]:<[2]> but was:<[1]> (0)", error.message)
}

@Test fun assertThat_failing_transformed_assert_shows_original_by_displayActual_lambda() {
val error = assertFails {
assertThat(0, name = "test", displayActual = { "Number:${it}" })
.assertThat(1).isEqualTo(2)
}

assertEquals("expected [test]:<[2]> but was:<[1]> (Number:0)", error.message)
}

@Test fun assertThat_on_failing_assert_is_ignored() {
val error = assertFails {
assertAll {
Expand Down

0 comments on commit a8c8b4b

Please sign in to comment.