mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-05 01:32:14 +00:00
Test with generated testcases which cover all paths
This commit is contained in:
parent
cf6f9f54fe
commit
4037feedba
@ -22,6 +22,8 @@ You should make sure the gradle commands shown above work when you execute them
|
||||
There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. What's unusual for the Java version is there are two executables listed in [config.gr](../texttests/config.gr) for Java. One uses Gradle wrapped in a python script, the other relies on your CLASSPATH being set correctly in [environment.gr](../texttests/environment.gr).
|
||||
|
||||
|
||||
## Starting Point
|
||||
|
||||
### Project info
|
||||
1. Code is highly unreadable and impossible to extend
|
||||
2. Algorithm and test cases are unknown, I do not trust that `TexttestFixtures` covers all paths so I want to validate it with jacoco
|
||||
@ -33,7 +35,8 @@ There are instructions in the [TextTest Readme](../texttests/README.md) for sett
|
||||
3. understand algorithm and propose better solution
|
||||
|
||||
|
||||
### Execution
|
||||
## Execution
|
||||
### Testing TexttestFixtures
|
||||
TexttestFixtures indeed do not cover all paths, based on jacoco coverage:
|
||||

|
||||
|
||||
@ -43,3 +46,13 @@ There are two ways to advance:
|
||||
|
||||
I prefer to go with (2) because it seems to me to be more resilient way. I need to check if execution
|
||||
time is not bloated. Also minimize number of test cases.
|
||||
|
||||
### Test cases generation
|
||||
I copied GildedRose class to PlatinumRose and implemented a test which executes both app
|
||||
and then compare results. Time of execution is under 500ms which is great. Also all paths are covered:
|
||||
|
||||

|
||||
|
||||
One noticeable fact i that items are declared as `var` which makes it possible to update during/after execution.
|
||||
In my opinion this is a design issue, code-smell and I'm going to change it to `val`.
|
||||
So far I only changed Item to data class so that object comparison is easier, also it seems to be DTO/record so it suppose to be a data class
|
||||
|
||||
BIN
Kotlin/img_1.png
Normal file
BIN
Kotlin/img_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@ -1,6 +1,6 @@
|
||||
package com.gildedrose
|
||||
|
||||
open class Item(var name: String, var sellIn: Int, var quality: Int) {
|
||||
data class Item(var name: String, var sellIn: Int, var quality: Int) {
|
||||
override fun toString(): String {
|
||||
return this.name + ", " + this.sellIn + ", " + this.quality
|
||||
}
|
||||
|
||||
58
Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt
Normal file
58
Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt
Normal file
@ -0,0 +1,58 @@
|
||||
package com.gildedrose
|
||||
|
||||
class PlatinumRose(var items: List<Item>) {
|
||||
|
||||
fun updateQuality() {
|
||||
for (i in items.indices) {
|
||||
if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert") {
|
||||
if (items[i].quality > 0) {
|
||||
if (items[i].name != "Sulfuras, Hand of Ragnaros") {
|
||||
items[i].quality = items[i].quality - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (items[i].quality < 50) {
|
||||
items[i].quality = items[i].quality + 1
|
||||
|
||||
if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") {
|
||||
if (items[i].sellIn < 11) {
|
||||
if (items[i].quality < 50) {
|
||||
items[i].quality = items[i].quality + 1
|
||||
}
|
||||
}
|
||||
|
||||
if (items[i].sellIn < 6) {
|
||||
if (items[i].quality < 50) {
|
||||
items[i].quality = items[i].quality + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items[i].name != "Sulfuras, Hand of Ragnaros") {
|
||||
items[i].sellIn = items[i].sellIn - 1
|
||||
}
|
||||
|
||||
if (items[i].sellIn < 0) {
|
||||
if (items[i].name != "Aged Brie") {
|
||||
if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") {
|
||||
if (items[i].quality > 0) {
|
||||
if (items[i].name != "Sulfuras, Hand of Ragnaros") {
|
||||
items[i].quality = items[i].quality - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items[i].quality = items[i].quality - items[i].quality
|
||||
}
|
||||
} else {
|
||||
if (items[i].quality < 50) {
|
||||
items[i].quality = items[i].quality + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
43
Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt
Normal file
43
Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package com.gildedrose
|
||||
|
||||
import java.util.*
|
||||
|
||||
|
||||
fun generateTestCasesInRanger(names: List<String>, sellInRange: IntRange, qualityRange: IntRange): List<Item> {
|
||||
val items = LinkedList<Item>()
|
||||
for (name in names) {
|
||||
for (sellIn in sellInRange) {
|
||||
for (quality in qualityRange) {
|
||||
items.add(Item(name, sellIn, quality))
|
||||
}
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val names = listOf(
|
||||
"Aged Brie",
|
||||
"Backstage passes to a TAFKAL80ETC concert",
|
||||
"Sulfuras, Hand of Ragnaros",
|
||||
"new none-existing on code name"
|
||||
)
|
||||
val sellInRange = -100..100
|
||||
val qualityRange = -100..100
|
||||
val allTestCases = generateTestCasesInRanger(names, sellInRange, qualityRange)
|
||||
|
||||
|
||||
var testCasesCounter = 0
|
||||
for (testCase in allTestCases) {
|
||||
val app = GildedRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality)))
|
||||
app.updateQuality()
|
||||
|
||||
|
||||
if (testCase.sellIn != app.items[0].sellIn || testCase.quality != app.items[0].quality) {
|
||||
//println("$testCasesCounter: $testCase vs ${app.items[0]}")
|
||||
testCasesCounter += 1
|
||||
}
|
||||
}
|
||||
println("Found $testCasesCounter testCasesCounter of out ${allTestCases.size} allTestCases")
|
||||
|
||||
}
|
||||
@ -6,15 +6,15 @@ fun main(args: Array<String>) {
|
||||
|
||||
val items = listOf(
|
||||
Item("+5 Dexterity Vest", 10, 20), //
|
||||
Item("Aged Brie", 2, 0), //
|
||||
Item("Elixir of the Mongoose", 5, 7), //
|
||||
Item("Sulfuras, Hand of Ragnaros", 0, 80), //
|
||||
Item("Sulfuras, Hand of Ragnaros", -1, 80),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 15, 20),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 10, 49),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 5, 49),
|
||||
// this conjured item does not work properly yet
|
||||
Item("Conjured Mana Cake", 3, 6)
|
||||
Item("Aged Brie", 2, 0), //
|
||||
Item("Elixir of the Mongoose", 5, 7), //
|
||||
Item("Sulfuras, Hand of Ragnaros", 0, 80), //
|
||||
Item("Sulfuras, Hand of Ragnaros", -1, 80),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 15, 20),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 10, 49),
|
||||
Item("Backstage passes to a TAFKAL80ETC concert", 5, 49),
|
||||
// this conjured item does not work properly yet
|
||||
Item("Conjured Mana Cake", 3, 6)
|
||||
)
|
||||
|
||||
val app = GildedRose(items)
|
||||
|
||||
@ -20,7 +20,16 @@ internal class GildedRoseTest {
|
||||
val app = GildedRose(items)
|
||||
app.updateQuality()
|
||||
assertEquals("foo", app.items[0].name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should execute without exception for empty list`() {
|
||||
val items = listOf<Item>()
|
||||
val app = GildedRose(items)
|
||||
app.updateQuality()
|
||||
assertAll(
|
||||
{ assertEquals(items, app.items) }
|
||||
)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ -53,7 +62,6 @@ internal class GildedRoseTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
48
Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt
Normal file
48
Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt
Normal file
@ -0,0 +1,48 @@
|
||||
package com.gildedrose
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.*
|
||||
|
||||
class PlatinumRoseTest {
|
||||
|
||||
@Test
|
||||
fun `should update quality for generated test cases and compare with GlidedRose`() {
|
||||
val names = listOf(
|
||||
"Aged Brie",
|
||||
"Backstage passes to a TAFKAL80ETC concert",
|
||||
"Sulfuras, Hand of Ragnaros",
|
||||
"new none-existing on code name"
|
||||
)
|
||||
val sellInRange = -100..100
|
||||
val qualityRange = -100..100
|
||||
val allTestCases = generateTestCasesInRanger(names, sellInRange, qualityRange)
|
||||
|
||||
|
||||
for (testCase in allTestCases) {
|
||||
val platinumRose = PlatinumRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality)))
|
||||
val gildedRose = GildedRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality)))
|
||||
|
||||
platinumRose.updateQuality()
|
||||
gildedRose.updateQuality()
|
||||
|
||||
assertEquals(gildedRose.items, platinumRose.items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateTestCasesInRanger(
|
||||
names: List<String>,
|
||||
sellInRange: IntRange,
|
||||
qualityRange: IntRange
|
||||
): List<Item> {
|
||||
val items = LinkedList<Item>()
|
||||
for (name in names) {
|
||||
for (sellIn in sellInRange) {
|
||||
for (quality in qualityRange) {
|
||||
items.add(Item(name, sellIn, quality))
|
||||
}
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user