From 679d14e5aec5f1d32585899247f9b1693de565dc Mon Sep 17 00:00:00 2001 From: Silvio D'Alessandro Date: Tue, 5 Jul 2022 11:11:58 +0200 Subject: [PATCH 1/2] Add support for flat extracting elements from an iterable --- CHANGELOG.md | 1 + .../kotlin/assertk/assertions/iterable.kt | 13 +++++++++++ .../test/assertk/assertions/IterableTest.kt | 23 ++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915ba2b4..377f9c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Added `first` and `single` assertion for `Iterable` +- Added `flatExtracting` assertion for `Iterable` ## [0.25] 2021-09-12 diff --git a/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt b/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt index ee685e68..7270b854 100644 --- a/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt +++ b/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt @@ -181,6 +181,19 @@ fun Assert>.extracting( actual.map { Triple(f1(it), f2(it), f3(it)) } } +/** + * Flat extracts a value from each item in the iterable, allowing you to assert on a flattened list of those values. + * + * ``` + * assertThat(people) + * .flatExtracting(Person::relatives) + * .contains("Sue", "Bob") + * ``` + */ +fun Assert>.flatExtracting(f1: (E) -> Iterable): Assert> = transform { actual -> + actual.flatMap(f1) +} + /** * Asserts on each item in the iterable, passing if none of the items pass. * The given lambda will be run for each item. diff --git a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt index 53a95f30..12319263 100644 --- a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt +++ b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt @@ -460,6 +460,27 @@ class IterableTest { } //region extracting + //region flatExtracting + @Test fun flat_extracting_function_passes() { + val thing = Thing("one", 2, '3', listOf("A", "B")) + assertThat(listOf(thing) as Iterable).flatExtracting { it.four }.containsExactly("A", "B") + } + + @Test fun flat_extracting_function_fails() { + val thing = Thing("one", 2, '3', listOf("A", "B")) + val error = assertFails { + assertThat(listOf(thing) as Iterable).flatExtracting { it.four }.containsExactly("C", "D") + } + assertEquals( + """expected to contain exactly:<["C", "D"]> but was:<["A", "B"]> + | at index:0 expected:<"C"> + | at index:0 unexpected:<"A"> + | at index:1 expected:<"D"> + | at index:1 unexpected:<"B"> ([Thing(one=one, two=2, three=3, four=[A, B])])""".trimMargin(), error.message + ) + } + //region flatExtracting + //region first @Test fun first_element_present_match_passes() { assertThat(listOf(1, 2)).first().isEqualTo(1) @@ -507,5 +528,5 @@ class IterableTest { } //endregion - data class Thing(val one: String, val two: Int, val three: Char) + data class Thing(val one: String, val two: Int, val three: Char, val four: List = listOf()) } \ No newline at end of file From d641a47a1fbdfda97ec257e443edc75d6c740e6b Mon Sep 17 00:00:00 2001 From: Silvio D'Alessandro Date: Wed, 6 Jul 2022 10:14:32 +0200 Subject: [PATCH 2/2] Fix tests --- .../test/assertk/assertions/IterableTest.kt | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt index 12319263..6852acd1 100644 --- a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt +++ b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt @@ -416,14 +416,14 @@ class IterableTest { } @Test fun pair_extracting_function_passes() { - assertThat(listOf(Thing("one", 1, '1'), Thing("two", 2, '2')) as Iterable) + assertThat(listOf(Thing("one", 1, listOf('1')), Thing("two", 2, listOf('2'))) as Iterable) .extracting(Thing::one, Thing::two) .containsExactly("one" to 1, "two" to 2) } @Test fun pair_extracting_function_fails() { val error = assertFails { - assertThat(listOf(Thing("one", 1, '1'), Thing("two", 2, '2')) as Iterable) + assertThat(listOf(Thing("one", 1, listOf('1')), Thing("two", 2, listOf('2'))) as Iterable) .extracting(Thing::one, Thing::two) .containsExactly("one" to 2, "two" to 1) } @@ -432,29 +432,29 @@ class IterableTest { | at index:0 expected:<("one", 2)> | at index:0 unexpected:<("one", 1)> | at index:1 expected:<("two", 1)> - | at index:1 unexpected:<("two", 2)> ([Thing(one=one, two=1, three=1), Thing(one=two, two=2, three=2)])""".trimMargin(), + | at index:1 unexpected:<("two", 2)> ([Thing(one=one, two=1, three=[1]), Thing(one=two, two=2, three=[2])])""".trimMargin(), error.message ) } @Test fun triple_extracting_function_passes() { - assertThat(listOf(Thing("one", 1, '1'), Thing("two", 2, '2')) as Iterable) + assertThat(listOf(Thing("one", 1, listOf('1')), Thing("two", 2, listOf('2'))) as Iterable) .extracting(Thing::one, Thing::two, Thing::three) - .containsExactly(Triple("one", 1, '1'), Triple("two", 2, '2')) + .containsExactly(Triple("one", 1, listOf('1')), Triple("two", 2, listOf('2'))) } @Test fun triple_extracting_function_fails() { val error = assertFails { - assertThat(listOf(Thing("one", 1, '1'), Thing("two", 2, '2')) as Iterable) + assertThat(listOf(Thing("one", 1, listOf('1')), Thing("two", 2, listOf('2'))) as Iterable) .extracting(Thing::one, Thing::two, Thing::three) - .containsExactly(Triple("one", 1, '2'), Triple("two", 2, '3')) + .containsExactly(Triple("one", 1, listOf('2')), Triple("two", 2, listOf('3'))) } assertEquals( - """expected to contain exactly:<[("one", 1, '2'), ("two", 2, '3')]> but was:<[("one", 1, '1'), ("two", 2, '2')]> - | at index:0 expected:<("one", 1, '2')> - | at index:0 unexpected:<("one", 1, '1')> - | at index:1 expected:<("two", 2, '3')> - | at index:1 unexpected:<("two", 2, '2')> ([Thing(one=one, two=1, three=1), Thing(one=two, two=2, three=2)])""".trimMargin(), + """expected to contain exactly:<[("one", 1, ['2']), ("two", 2, ['3'])]> but was:<[("one", 1, ['1']), ("two", 2, ['2'])]> + | at index:0 expected:<("one", 1, ['2'])> + | at index:0 unexpected:<("one", 1, ['1'])> + | at index:1 expected:<("two", 2, ['3'])> + | at index:1 unexpected:<("two", 2, ['2'])> ([Thing(one=one, two=1, three=[1]), Thing(one=two, two=2, three=[2])])""".trimMargin(), error.message ) } @@ -462,21 +462,21 @@ class IterableTest { //region flatExtracting @Test fun flat_extracting_function_passes() { - val thing = Thing("one", 2, '3', listOf("A", "B")) - assertThat(listOf(thing) as Iterable).flatExtracting { it.four }.containsExactly("A", "B") + val thing = Thing("one", 2, listOf('A', 'B')) + assertThat(listOf(thing) as Iterable).flatExtracting { it.three }.containsExactly('A', 'B') } @Test fun flat_extracting_function_fails() { - val thing = Thing("one", 2, '3', listOf("A", "B")) + val thing = Thing("one", 2, listOf('A', 'B')) val error = assertFails { - assertThat(listOf(thing) as Iterable).flatExtracting { it.four }.containsExactly("C", "D") + assertThat(listOf(thing) as Iterable).flatExtracting { it.three }.containsExactly('C', 'D') } assertEquals( - """expected to contain exactly:<["C", "D"]> but was:<["A", "B"]> - | at index:0 expected:<"C"> - | at index:0 unexpected:<"A"> - | at index:1 expected:<"D"> - | at index:1 unexpected:<"B"> ([Thing(one=one, two=2, three=3, four=[A, B])])""".trimMargin(), error.message + """expected to contain exactly:<['C', 'D']> but was:<['A', 'B']> + | at index:0 expected:<'C'> + | at index:0 unexpected:<'A'> + | at index:1 expected:<'D'> + | at index:1 unexpected:<'B'> ([Thing(one=one, two=2, three=[A, B])])""".trimMargin(), error.message ) } //region flatExtracting @@ -528,5 +528,5 @@ class IterableTest { } //endregion - data class Thing(val one: String, val two: Int, val three: Char, val four: List = listOf()) + data class Thing(val one: String, val two: Int, val three: List) } \ No newline at end of file