Skip to content

Instantly share code, notes, and snippets.

@efenderbosch
Last active February 4, 2022 17:34
Show Gist options
  • Select an option

  • Save efenderbosch/f502c1518f6a2c1b636273633ef3ad2f to your computer and use it in GitHub Desktop.

Select an option

Save efenderbosch/f502c1518f6a2c1b636273633ef3ad2f to your computer and use it in GitHub Desktop.
Fluent equal to ignoring for Strikt
package util
import org.junit.jupiter.api.Test
import strikt.api.Assertion
import strikt.api.expectThat
import strikt.assertions.contentEquals
import strikt.assertions.isEqualTo
import java.beans.Introspector
import java.util.*
import kotlin.reflect.KProperty1
typealias IgnoringBuilder<T> = Assertion.Builder<Pair<T, List<KProperty1<T, Any>>>>
fun <T> Assertion.Builder<T>.ignoring(vararg properties: KProperty1<T, Any>): IgnoringBuilder<T> =
get { subject to properties.toList() }
fun <T : Any> IgnoringBuilder<T>.isEqualTo(expected: T): IgnoringBuilder<T> =
compose(description(), expected) { subject ->
Introspector.getBeanInfo(subject.first::class.java).let { beanInfo ->
beanInfo
.propertyDescriptors
.filter { it.name != "class" && it.name !in subject.second.map { prop -> prop.name } }
.forEach { property ->
val mappedAssertion = get("value of property ${property.name}") {
property.readMethod(first)
}
val otherValue = property.readMethod(expected)
@Suppress("UNCHECKED_CAST")
when {
property.propertyType == BooleanArray::class.java ->
(mappedAssertion as Assertion.Builder<BooleanArray>).contentEquals(otherValue as BooleanArray)
property.propertyType == ByteArray::class.java ->
(mappedAssertion as Assertion.Builder<ByteArray>).contentEquals(otherValue as ByteArray)
property.propertyType == ShortArray::class.java ->
(mappedAssertion as Assertion.Builder<ShortArray>).contentEquals(otherValue as ShortArray)
property.propertyType == IntArray::class.java ->
(mappedAssertion as Assertion.Builder<IntArray>).contentEquals(otherValue as IntArray)
property.propertyType == LongArray::class.java ->
(mappedAssertion as Assertion.Builder<LongArray>).contentEquals(otherValue as LongArray)
property.propertyType == FloatArray::class.java ->
(mappedAssertion as Assertion.Builder<FloatArray>).contentEquals(otherValue as FloatArray)
property.propertyType == DoubleArray::class.java ->
(mappedAssertion as Assertion.Builder<DoubleArray>).contentEquals(otherValue as DoubleArray)
property.propertyType.isArray ->
(mappedAssertion as Assertion.Builder<Array<*>>).contentEquals(otherValue as Array<*>)
else -> mappedAssertion.isEqualTo(otherValue)
}
}
}
} then {
if (allPassed) pass() else fail()
}
fun <T : Any> IgnoringBuilder<T>.unwrap(): Assertion.Builder<T> = get { first }
private fun <T> IgnoringBuilder<T>.description() =
"is equal field-by-field ignoring ${subject.second.joinToString(",", "[", "]") { it.name }} to %s"
class IgnoringBuilderTests {
@Test
fun test() {
val foo1 = Foo("qux", UUID.randomUUID())
val foo2 = Foo("qux", UUID.randomUUID())
expectThat(foo1).ignoring(Foo::baz).isEqualTo(foo2).unwrap().and {
// continue using foo1...
}
}
}
data class Foo(val bar: String, val baz: UUID)
@robfletcher
Copy link

robfletcher commented Feb 4, 2022

You could always structure the assertion as

expectThat(foo1) {
  ignoring(Foo::baz).isEqualTo(foo2)
  // continue using foo1...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment