Skip to content

Instantly share code, notes, and snippets.

@Lamartio
Created June 11, 2018 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lamartio/de6b3ab70084f5a6e91c9f077b22b526 to your computer and use it in GitHub Desktop.
Save Lamartio/de6b3ab70084f5a6e91c9f077b22b526 to your computer and use it in GitHub Desktop.
Simplified Spek for creating tests without the need of additional dependencies
private val Store<Car>.frontLeftWheel: Wheel
get() = getState().wheels.entries.first { it.key == Position.FRONT_LEFT }.value
class ChangeWheelFeature : Feature({
lateinit var store: Store<Car>
given("the wheel is a 15 inch one") {
setUp { store = newStore() }
on("changing the wheel with a bigger one") {
WheelAction.Change(Position.FRONT_LEFT, 17).let(store::dispatch)
it("should have a wheel of 17 inch") { store.frontLeftWheel.size shouldBe 17 }
}
on("changing the wheel with a smaller one") {
WheelAction.Change(Position.FRONT_LEFT, 13).let(store::dispatch)
it("should have a wheel of 13 inch") { store.frontLeftWheel.size shouldBe 13 }
}
}
})
import org.hamcrest.Matcher
import org.junit.Assert
import org.junit.Ignore
import org.junit.Test
@Ignore
open class Feature(private val block: Feature.() -> Unit = {}) : () -> Unit {
@Test
override fun invoke() {
block()
}
}
infix fun <T> T.shouldBe(expected: T) = Assert.assertEquals(expected, this)
infix fun <T> T.shouldBe(expected: Matcher<T>) = Assert.assertThat(this, expected)
fun given(text: String, block: Given.() -> Unit) {
Given(text, block)
}
class Given internal constructor(
text: String,
block: Given.() -> Unit = {}
) : FeatureComponent("\n\nGiven $text") {
private var setUp: Given.() -> Unit = {}
private var tearDown: Given.() -> Unit = {}
init {
block()
}
fun setUp(block: Given.() -> Unit) {
setUp = setUp.let { previous -> { previous(); block() } }
}
fun tearDown(block: Given.() -> Unit) {
tearDown = tearDown.let { previous -> { previous(); block() } }
}
fun on(text: String, block: On.() -> Unit = {}): On =
On(this, setUp, tearDown, text, block).also { conditions.add(it) }
override fun toString(): String = "$text\n\n${super.toString()}"
}
class On internal constructor(
private val given: Given,
setUp: Given.() -> Unit = {},
tearDown: Given.() -> Unit = {},
text: String,
block: On.() -> Unit = {}
) : FeatureComponent(text = "On $text") {
init {
given.run {
setUp()
block()
tearDown()
}
}
fun it(text: String, block: It.() -> Unit = {}): It =
It(this, text, block).also { conditions.add(it) }
override fun toString(): String = "$given\n$text\n${super.toString()}"
}
class It internal constructor(
private val on: On,
text: String,
block: It.() -> Unit = {}
) : FeatureComponent(text = "It $text") {
init {
try {
block()
} catch (error: AssertionError) {
throw AssertionError(toString(), error)
}
}
override fun toString(): String = "$on\n$text\n${super.toString()}"
}
open class FeatureComponent internal constructor(
protected val text: String,
private val depth: Int = 0
) {
protected val conditions: MutableList<FeatureComponent> = mutableListOf()
fun and(text: String): FeatureComponent =
apply { conditions.add(FeatureComponent("And $text", depth + 1)) }
fun or(text: String): FeatureComponent =
apply { conditions.add(FeatureComponent("Or $text", depth + 1)) }
override fun toString(): String =
flattenConditions()
.toMutableList()
.also { it.add(this) }
.joinToString(
separator = "\n",
transform = { getIndent(it.depth) + it.text }
)
private fun getIndent(depth: Int) =
0.until(depth)
.map { "\t" }
.toMutableList()
.apply { add("") }
.reduce { acc, s -> acc + s }
private fun flattenConditions(): List<FeatureComponent> =
conditions + conditions.flatMap {
if (it.conditions.isNotEmpty()) it.flattenConditions()
else emptyList()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment