Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade KotlinTest to Kotest #2405

Merged
merged 11 commits into from
May 21, 2021
2 changes: 1 addition & 1 deletion arrow-libs/ank/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ systemProp.org.gradle.internal.publish.checksums.insecure=true
kotlin.incremental=true
# Kotlin Test configuration
#Parallelism needs to be set to 1 since the concurrent tests in arrow-effects become flaky otherwise
kotlintest.parallelism=1
kotest.framework.parallelism=1

# Reason: https://youtrack.jetbrains.com/issue/KT-46847
# kotlin.stdlib.default.dependency=false
1 change: 0 additions & 1 deletion arrow-libs/core/arrow-continuations/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ kotlin {
jvmTest {
dependencies {
runtimeOnly "org.junit.vintage:junit-vintage-engine:$JUNIT_VINTAGE_VERSION"
compileOnly "io.kotlintest:kotlintest-runner-junit5:$KOTLIN_TEST_VERSION", excludeArrow
implementation project(":arrow-core-test")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package generic
import arrow.core.computations.either
import arrow.core.Either.Right
import arrow.core.Either.Left
import io.kotlintest.fail
import io.kotlintest.shouldBe
import io.kotlintest.shouldThrow
import io.kotlintest.specs.StringSpec
import io.kotest.assertions.fail
import io.kotest.matchers.shouldBe
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.StringSpec
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import arrow.continuations.generic.RestrictedScope
import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.test.UnitSpec
import io.kotlintest.shouldBe
import io.kotest.matchers.shouldBe

abstract class ContTestSuite : UnitSpec() {
abstract suspend fun <A> runScope(func: (suspend RestrictedScope<A>.() -> A)): A
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import arrow.core.test.UnitSpec
import arrow.retrofit.adapter.mock.ErrorMock
import arrow.retrofit.adapter.mock.ResponseMock
import arrow.retrofit.adapter.retrofit.SuspedApiClientTest
import io.kotlintest.Spec
import io.kotlintest.shouldBe
import kotlinx.coroutines.runBlocking
import io.kotest.matchers.shouldBe
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.SocketPolicy
Expand All @@ -28,46 +26,39 @@ class ArrowEitherCallAdapterTest : UnitSpec() {
.create(SuspedApiClientTest::class.java)
}

override fun beforeSpec(spec: Spec) {
super.beforeSpec(spec)
server.start()
}

override fun afterSpec(spec: Spec) {
server.shutdown()
super.afterSpec(spec)
}

init {

beforeSpec { server.start() }
afterSpec { server.shutdown() }

"should return ResponseMock for 200 with valid JSON" {
server.enqueue(MockResponse().setBody("""{"response":"Arrow rocks"}"""))

val body = runBlocking { service.getEither() }
val body = service.getEither()

body shouldBe ResponseMock("Arrow rocks").right()
}

"should return ErrorMock for 400 with valid JSON" {
server.enqueue(MockResponse().setBody("""{"errorCode":666}""").setResponseCode(400))

val body = runBlocking { service.getEither() }
val body = service.getEither()

body shouldBe ErrorMock(666).left()
}

"should throw for 200 with invalid JSON" {
server.enqueue(MockResponse().setBody("""not a valid JSON"""))

val body = kotlin.runCatching { service.getEither() }
val body = runCatching { service.getEither() }

body.isFailure shouldBe true
}

"should throw for 400 and invalid JSON" {
server.enqueue(MockResponse().setBody("""not a valid JSON""").setResponseCode(400))

val body = kotlin.runCatching { service.getEither() }
val body = runCatching { service.getEither() }

body.isFailure shouldBe true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import arrow.core.test.UnitSpec
import arrow.retrofit.adapter.mock.ErrorMock
import arrow.retrofit.adapter.mock.ResponseMock
import arrow.retrofit.adapter.retrofit.SuspedApiClientTest
import io.kotlintest.Spec
import io.kotlintest.shouldBe
import kotlinx.coroutines.runBlocking
import io.kotest.matchers.shouldBe
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.SocketPolicy
Expand All @@ -28,22 +26,15 @@ class ArrowResponseEAdapterTest : UnitSpec() {
.create(SuspedApiClientTest::class.java)
}

override fun beforeSpec(spec: Spec) {
super.beforeSpec(spec)
server.start()
}

override fun afterSpec(spec: Spec) {
server.shutdown()
super.afterSpec(spec)
}

init {

beforeSpec { server.start() }
afterSpec { server.shutdown() }

"should return ResponseMock for 200 with valid JSON" {
server.enqueue(MockResponse().setBody("""{"response":"Arrow rocks"}"""))

val responseE = runBlocking { service.getResponseE() }
val responseE = service.getResponseE()

with(responseE) {
code shouldBe 200
Expand All @@ -54,7 +45,7 @@ class ArrowResponseEAdapterTest : UnitSpec() {
"should return ErrorMock for 400 with valid JSON" {
server.enqueue(MockResponse().setBody("""{"errorCode":42}""").setResponseCode(400))

val responseE = runBlocking { service.getResponseE() }
val responseE = service.getResponseE()

with(responseE) {
code shouldBe 400
Expand All @@ -65,15 +56,15 @@ class ArrowResponseEAdapterTest : UnitSpec() {
"should throw for 200 with invalid JSON" {
server.enqueue(MockResponse().setBody("""not a valid JSON"""))

val responseE = kotlin.runCatching { service.getResponseE() }
val responseE = runCatching { service.getResponseE() }

responseE.isFailure shouldBe true
}

"should throw for 400 and invalid JSON" {
server.enqueue(MockResponse().setBody("""not a valid JSON""").setResponseCode(400))

val responseE = kotlin.runCatching { service.getResponseE() }
val responseE = runCatching { service.getResponseE() }

responseE.isFailure shouldBe true
}
Expand Down
5 changes: 3 additions & 2 deletions arrow-libs/core/arrow-core-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies {
api project(":arrow-core")
api project(":arrow-continuations")
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$KOTLINX_COROUTINES_VERSION"
api "io.kotlintest:kotlintest-runner-junit5:$KOTLIN_TEST_VERSION", excludeArrow
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$JUNIT_VINTAGE_VERSION"
api "io.kotest:kotest-runner-junit5-jvm:$KOTEST_VERSION" // for kotest framework
api "io.kotest:kotest-assertions-core-jvm:$KOTEST_VERSION" // for kotest core jvm assertions
api "io.kotest:kotest-property-jvm:$KOTEST_VERSION" // for kotest property test
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,95 +3,102 @@ package arrow.core.test
import arrow.core.Tuple4
import arrow.core.Tuple5
import arrow.core.test.laws.Law
import io.kotlintest.TestCase
import io.kotlintest.TestType
import io.kotlintest.properties.Gen
import io.kotlintest.properties.PropertyContext
import io.kotlintest.properties.assertAll
import io.kotlintest.shouldBe
import io.kotlintest.specs.AbstractStringSpec
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.createTestName
import io.kotest.property.Arb
import io.kotest.property.PropertyContext
import io.kotest.property.arbitrary.bind
import io.kotest.property.checkAll

/**
* Base class for unit tests
*/
abstract class UnitSpec : AbstractStringSpec() {
abstract class UnitSpec : StringSpec() {

private val lawTestCases = mutableListOf<TestCase>()

fun testLaws(vararg laws: List<Law>): List<TestCase> = laws
fun testLaws(vararg laws: List<Law>): Unit = laws
.flatMap { list: List<Law> -> list.asIterable() }
.distinctBy { law: Law -> law.name }
.map { law: Law ->
val lawTestCase = createTestCase(law.name, law.test, defaultTestCaseConfig, TestType.Test)
lawTestCases.add(lawTestCase)
lawTestCase
.forEach { law: Law ->
registration().addTest(createTestName(law.name), xdisabled = false, law.test)
}

override fun testCases(): List<TestCase> = super.testCases() + lawTestCases
fun testLaws(prefix: String, vararg laws: List<Law>): Unit = laws
.flatMap { list: List<Law> -> list.asIterable() }
.distinctBy { law: Law -> law.name }
.forEach { law: Law ->
registration().addTest(createTestName(prefix, law.name, true), xdisabled = false, law.test)
}

fun <A, B, C, D, E, F, G> forAll(
gena: Gen<A>,
genb: Gen<B>,
genc: Gen<C>,
gend: Gen<D>,
gene: Gen<E>,
genf: Gen<F>,
geng: Gen<G>,
fn: PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G) -> Boolean
suspend fun <A, B, C, D, E, F, G> checkAll(
gena: Arb<A>,
genb: Arb<B>,
genc: Arb<C>,
gend: Arb<D>,
gene: Arb<E>,
genf: Arb<F>,
geng: Arb<G>,
fn: suspend PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G) -> Unit
) {
assertAll(gena, genb, genc, gend, gene, Gen.bind(genf, geng, ::Pair)) { a, b, c, d, e, (f, g) ->
fn(a, b, c, d, e, f, g) shouldBe true
checkAll(gena, genb, genc, gend, gene, Arb.bind(genf, geng, ::Pair)) { a, b, c, d, e, (f, g) ->
fn(a, b, c, d, e, f, g)
}
}

fun <A, B, C, D, E, F, G, H> forAll(
gena: Gen<A>,
genb: Gen<B>,
genc: Gen<C>,
gend: Gen<D>,
gene: Gen<E>,
genf: Gen<F>,
geng: Gen<G>,
genh: Gen<H>,
fn: PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) -> Boolean
suspend fun <A, B, C, D, E, F, G, H> checkAll(
gena: Arb<A>,
genb: Arb<B>,
genc: Arb<C>,
gend: Arb<D>,
gene: Arb<E>,
genf: Arb<F>,
geng: Arb<G>,
genh: Arb<H>,
fn: suspend PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) -> Unit
) {
assertAll(gena, genb, genc, gend, gene, Gen.bind(genf, geng, genh, ::Triple)) { a, b, c, d, e, (f, g, h) ->
fn(a, b, c, d, e, f, g, h) shouldBe true
checkAll(gena, genb, genc, gend, gene, Arb.bind(genf, geng, genh, ::Triple)) { a, b, c, d, e, (f, g, h) ->
fn(a, b, c, d, e, f, g, h)
}
}

fun <A, B, C, D, E, F, G, H, I> forAll(
gena: Gen<A>,
genb: Gen<B>,
genc: Gen<C>,
gend: Gen<D>,
gene: Gen<E>,
genf: Gen<F>,
geng: Gen<G>,
genh: Gen<H>,
geni: Gen<I>,
fn: PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) -> Boolean
suspend fun <A, B, C, D, E, F, G, H, I> checkAll(
gena: Arb<A>,
genb: Arb<B>,
genc: Arb<C>,
gend: Arb<D>,
gene: Arb<E>,
genf: Arb<F>,
geng: Arb<G>,
genh: Arb<H>,
geni: Arb<I>,
fn: suspend PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) -> Unit
) {
assertAll(gena, genb, genc, gend, gene, Gen.bind(genf, geng, genh, geni, ::Tuple4)) { a, b, c, d, e, (f, g, h, i) ->
fn(a, b, c, d, e, f, g, h, i) shouldBe true
checkAll(gena, genb, genc, gend, gene, Arb.bind(genf, geng, genh, geni, ::Tuple4)) { a, b, c, d, e, (f, g, h, i) ->
fn(a, b, c, d, e, f, g, h, i)
}
}

fun <A, B, C, D, E, F, G, H, I, J> forAll(
gena: Gen<A>,
genb: Gen<B>,
genc: Gen<C>,
gend: Gen<D>,
gene: Gen<E>,
genf: Gen<F>,
geng: Gen<G>,
genh: Gen<H>,
geni: Gen<I>,
genj: Gen<J>,
fn: PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) -> Boolean
suspend fun <A, B, C, D, E, F, G, H, I, J> checkAll(
gena: Arb<A>,
genb: Arb<B>,
genc: Arb<C>,
gend: Arb<D>,
gene: Arb<E>,
genf: Arb<F>,
geng: Arb<G>,
genh: Arb<H>,
geni: Arb<I>,
genj: Arb<J>,
fn: suspend PropertyContext.(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) -> Unit
) {
assertAll(gena, genb, genc, gend, gene, Gen.bind(genf, geng, genh, geni, genj, ::Tuple5)) { a, b, c, d, e, (f, g, h, i, j) ->
fn(a, b, c, d, e, f, g, h, i, j) shouldBe true
checkAll(
gena,
genb,
genc,
gend,
gene,
Arb.bind(genf, geng, genh, geni, genj, ::Tuple5)
) { a, b, c, d, e, (f, g, h, i, j) ->
fn(a, b, c, d, e, f, g, h, i, j)
}
}
}
Loading