Would be nice to have a version of `propertiesAreE...
# strikt
e
Would be nice to have a version of
propertiesAreEqualTo
that doesn’t require
other
to be the same type as the subject. Just a couple minor tweaks.
Copy code
fun <T : Any> Assertion.Builder<T>.reflectionEquals(
    other: Any,
    ignoredPropertyNames: List<String> = emptyList()
): Assertion.Builder<T> = compose("is equal field-by-field to %s", other) { subject ->
    val otherBeanInfo = Introspector.getBeanInfo(other.javaClass)
    val beanInfo = Introspector.getBeanInfo(subject.javaClass)
    beanInfo.propertyDescriptors
        .filter { it.name != "class" && it.name !in ignoredPropertyNames }
        .forEach { property ->
            val mappedAssertion = get("value of property ${property.name}") {
                property.readMethod(this)
            }
            val otherProperty = otherBeanInfo.propertyDescriptors.firstOrNull { it.name == property.name }
            val otherValue = otherProperty?.let { it.readMethod(other) }
// the rest is the same as propertiesAreEqualTo
useful for testing transformations from type A to type B. like a JPA entity to GQL or vice versa, etc
r
makes sense, yeah
if you open a PR I can add that in. I was planning to do a release some time soon-ish
e
ok, polishing up a bit now. adding the ability to ignore or fail for properties on the target that aren’t on the subject
r
neat
e
started the PR, but i’m getting this on build. don’t have time to continue on it this morning
r
I’ll have a look & see what’s going on there
e
i’ll drop from our VPN and try after hours as well. could be something strange w/ maven central
r
yeah, maybe. It’s certainly working locally for me
(I’m also on a work VPN here 🤷 )
e
so i’ve been swamped and completely forgot about this. i might get to it thurs evening
d
Hi @Eric, can you please post me the rest of your code -- it seems like this is just what I need right now...
e
Copy code
// based on <https://github.com/robfletcher/strikt/blob/main/strikt-jvm/src/main/kotlin/strikt/java/Any.kt>
fun <T : Any> Assertion.Builder<T>.reflectionEquals(
    other: Any,
    ignoredPropertyNames: List<String> = emptyList(),
    ignoreExtraProperties: Boolean = true
): Assertion.Builder<T> = compose("is equal field-by-field to %s", other) { subject ->
    val otherBeanInfo = Introspector.getBeanInfo(other.javaClass)
    val beanInfo = Introspector.getBeanInfo(subject.javaClass)
    beanInfo.propertyDescriptors
        .filter { it.name != "class" && it.name !in ignoredPropertyNames }
        .forEach { property ->
            val otherProperty = otherBeanInfo.propertyDescriptors.firstOrNull { it.name == property.name }
            if (otherProperty == null) {
                assertThat("has property: %s", property.name) { false }
                return@forEach
            }
            val mappedAssertion = get("value of property ${property.name}") { property.readMethod(subject) }
            val otherValue = otherProperty.let { it.readMethod(other) }
            @Suppress("UNCHECKED_CAST")
            when {
                mappedAssertion.subject != null && otherValue == null ->
                    mappedAssertion.assertThat("${property.name} is not null") { false }

                mappedAssertion.subject == null && otherValue != null ->
                    mappedAssertion.assertThat("${property.name} is null") { false }

                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<*>)

                property.propertyType.isPrimitive -> mappedAssertion.isEqualTo(otherValue)

                else -> mappedAssertion.reflectionEquals(
                    otherValue,
                    ignoredPropertyNames.map { it.substringAfter('.', "") }.filterNot { it.isEmpty() },
                    ignoreExtraProperties
                )
            }
        }
    if (!ignoreExtraProperties) {
        val extraProperties = otherBeanInfo.propertyDescriptors.map { it.name }.toSet() -
                beanInfo.propertyDescriptors.map { it.name }.toSet()
        assertThat("has no extra properties; found: %s", extraProperties) { extraProperties.isEmpty() }
    }
} then {
    if (allPassed) pass() else fail()
}
trying to pick up the PR again, on a new laptop…
do i need to compile w/ java 11?
r
I think something forced that to be the minimum yeah
lemme check real quick
I held off upgrading the Spring dependency recently because it seemed to force Java 17+
hmm, there was no reason it had to be set to 11 as a minimum. Changed it to 8 and I’ll push that to main now
e
The arrow module failed on 8
I'm using jenv and have multiple installed, so whatever is fine
r
Huh weird. It passed locally and on CI
But I also don’t have any reason I need to support < 11