From 8c188931a35833d640f56394047d5fdedeabe2a5 Mon Sep 17 00:00:00 2001 From: LeoHelfferich Date: Tue, 21 Jan 2025 15:47:22 +0100 Subject: [PATCH 1/3] BAEL-8537: formatting implementations and test coverage --- core-kotlin-modules/core-kotlin-12/pom.xml | 21 +++++++++ .../com/baeldung/formatting/NumberFormat.kt | 46 +++++++++++++++++++ .../formatting/NumberFormatUnitTest.kt | 45 ++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt create mode 100644 core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-12/pom.xml b/core-kotlin-modules/core-kotlin-12/pom.xml index e1572ace7..3202e2166 100644 --- a/core-kotlin-modules/core-kotlin-12/pom.xml +++ b/core-kotlin-modules/core-kotlin-12/pom.xml @@ -14,6 +14,21 @@ 1.0.0-SNAPSHOT + + + io.kotest + kotest-runner-junit5 + ${kotest.version} + test + + + io.kotest + kotest-property-jvm + ${kotest.version} + test + + + src/main/kotlin src/test/kotlin @@ -34,4 +49,10 @@ + + + 5.9.0 + 2.22.2 + + diff --git a/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt b/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt new file mode 100644 index 000000000..4d298bac5 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt @@ -0,0 +1,46 @@ +package com.baeldung.formatting + +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import java.util.* + +/** + * Interface for the different formatting implementation. + */ +interface NumberFormat { + + /** + * Returns provided number as string with thousands separator. + * + * 1000 -> '1.000' + * 750000 -> '750.000' + */ + fun formatted(number: Int): String +} + +object FormatByDecimalFormat : NumberFormat { + override fun formatted(number: Int): String = + DecimalFormat("#,###") + .format(number) + .replace(",", ".") +} + +object FormatByDecimalFormatGermany : NumberFormat { + override fun formatted(number: Int): String = + DecimalFormat("#,###", DecimalFormatSymbols(Locale.GERMANY)) + .format(number) +} + +object FormatByStringFormat : NumberFormat { + override fun formatted(number: Int): String = + String.format(Locale.GERMANY, "%,d", number) +} + +object FormatByChunking : NumberFormat { + override fun formatted(number: Int): String = + number.toString() + .reversed() // 15000 -> 00051 + .chunked(3) // [000] [51] + .joinToString(".") // 000.51 + .reversed() // 15.000 +} diff --git a/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt new file mode 100644 index 000000000..26c50f48a --- /dev/null +++ b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt @@ -0,0 +1,45 @@ +package com.baeldung.formatting + +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.property.Arb +import io.kotest.property.arbitrary.positiveInt +import io.kotest.property.checkAll +import org.assertj.core.api.Assertions.assertThat + +class NumberFormatUnitTest : ShouldSpec({ + + listOf( // different implementations of the formatting feature + "FormatByChunking" to { it: Int -> FormatByChunking.formatted(it) }, + "FormatByStringFormat" to { it: Int -> FormatByStringFormat.formatted(it) }, + "FormatByDecimalFormatGermany" to { it: Int -> FormatByDecimalFormatGermany.formatted(it) }, + "FormatByDecimalFormat" to { it: Int -> FormatByDecimalFormat.formatted(it) } + ).forEach { (type, function) -> + + // Property based test (for each implementation) + should("return correctly formatted string with $type") { + checkAll(Arb.positiveInt()) { number -> + var result = function(number) + + assertThat(result).containsPattern("^(\\d{1,3}(\\.\\d{3})*|\\d+)$") + assertThat(number.toString()).isEqualTo(result.replace(".", "")) + if (number > 999) assertThat(result).contains(".") + else assertThat(result).doesNotContain(".") + } + } + + listOf( // examples with given number and expected string + 100_000 to "100.000", + 1_234_567 to "1.234.567", + 0 to "0", + 12 to "12", + 456 to "456" + ).forEach { (number, expected) -> + + // Parameterised; Example based test + should("return expected string '$expected' for $number with $type") { + assertThat(function(number)).isEqualTo(expected) + } + } + + } +}) From 4d1135c8419d38ff68c7ac78c4072c46e5d0461c Mon Sep 17 00:00:00 2001 From: LeoHelfferich Date: Wed, 22 Jan 2025 08:18:39 +0100 Subject: [PATCH 2/3] BAEL-8537: test docs --- .../formatting/NumberFormatUnitTest.kt | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt index 26c50f48a..07e4896d0 100644 --- a/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt +++ b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt @@ -8,38 +8,49 @@ import org.assertj.core.api.Assertions.assertThat class NumberFormatUnitTest : ShouldSpec({ - listOf( // different implementations of the formatting feature - "FormatByChunking" to { it: Int -> FormatByChunking.formatted(it) }, - "FormatByStringFormat" to { it: Int -> FormatByStringFormat.formatted(it) }, - "FormatByDecimalFormatGermany" to { it: Int -> FormatByDecimalFormatGermany.formatted(it) }, - "FormatByDecimalFormat" to { it: Int -> FormatByDecimalFormat.formatted(it) } - ).forEach { (type, function) -> + // Dynamic generation of tests for each implementation + nameToImplementationPairs.forEach { (type, function) -> // Property based test (for each implementation) - should("return correctly formatted string with $type") { + should("return correctly formatted string by $type") { checkAll(Arb.positiveInt()) { number -> var result = function(number) + // Check with regex assertThat(result).containsPattern("^(\\d{1,3}(\\.\\d{3})*|\\d+)$") + // Check against original, by removing the separators assertThat(number.toString()).isEqualTo(result.replace(".", "")) + + // Check for general presence & absence of separators if (number > 999) assertThat(result).contains(".") else assertThat(result).doesNotContain(".") } } - listOf( // examples with given number and expected string - 100_000 to "100.000", - 1_234_567 to "1.234.567", - 0 to "0", - 12 to "12", - 456 to "456" - ).forEach { (number, expected) -> + givenToExpectedPairs.forEach { (number, expected) -> // Parameterised; Example based test - should("return expected string '$expected' for $number with $type") { + should("return expected string '$expected' for $number by $type") { assertThat(function(number)).isEqualTo(expected) } } } }) + +// Examples to check against, with given number and expected string +private val givenToExpectedPairs = listOf( + 0 to "0", + 12 to "12", + 456 to "456", + 100_000 to "100.000", + 1_234_567 to "1.234.567" +) + +// Different implementations of the formatting feature with the display name +private val nameToImplementationPairs = listOf( + "FormatByChunking" to { it: Int -> FormatByChunking.formatted(it) }, + "FormatByStringFormat" to { it: Int -> FormatByStringFormat.formatted(it) }, + "FormatByDecimalFormatGermany" to { it: Int -> FormatByDecimalFormatGermany.formatted(it) }, + "FormatByDecimalFormat" to { it: Int -> FormatByDecimalFormat.formatted(it) } +) From dbc117292f23998ae690b03c4b888ad667a1b730 Mon Sep 17 00:00:00 2001 From: LeoHelfferich Date: Fri, 24 Jan 2025 11:21:23 +0100 Subject: [PATCH 3/3] BAEL-8537: adaptation to article --- .../com/baeldung/formatting/NumberFormat.kt | 3 --- .../baeldung/formatting/NumberFormatUnitTest.kt | 16 +++++----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt b/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt index 4d298bac5..b06711c01 100644 --- a/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt +++ b/core-kotlin-modules/core-kotlin-12/src/main/kotlin/com/baeldung/formatting/NumberFormat.kt @@ -11,9 +11,6 @@ interface NumberFormat { /** * Returns provided number as string with thousands separator. - * - * 1000 -> '1.000' - * 750000 -> '750.000' */ fun formatted(number: Int): String } diff --git a/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt index 07e4896d0..c99970104 100644 --- a/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt +++ b/core-kotlin-modules/core-kotlin-12/src/test/kotlin/com/baeldung/formatting/NumberFormatUnitTest.kt @@ -9,29 +9,23 @@ import org.assertj.core.api.Assertions.assertThat class NumberFormatUnitTest : ShouldSpec({ // Dynamic generation of tests for each implementation - nameToImplementationPairs.forEach { (type, function) -> + nameToImplementationPairs.forEach { (name, function) -> // Property based test (for each implementation) - should("return correctly formatted string by $type") { + should("return correctly formatted string with $name implementation") { checkAll(Arb.positiveInt()) { number -> var result = function(number) - // Check with regex assertThat(result).containsPattern("^(\\d{1,3}(\\.\\d{3})*|\\d+)$") - // Check against original, by removing the separators assertThat(number.toString()).isEqualTo(result.replace(".", "")) - - // Check for general presence & absence of separators - if (number > 999) assertThat(result).contains(".") - else assertThat(result).doesNotContain(".") } } - givenToExpectedPairs.forEach { (number, expected) -> + givenToExpectedPairs.forEach { (givenNumber, expectedString) -> // Parameterised; Example based test - should("return expected string '$expected' for $number by $type") { - assertThat(function(number)).isEqualTo(expected) + should("return '$expectedString' for $givenNumber with $name implementation") { + assertThat(function(givenNumber)).isEqualTo(expectedString) } }