Skip to content

Consolidate ExpectFailureException and AssumptionFailedException to skip the test #4883

@Virtlink

Description

@Virtlink

I want to be able write tests that can skip themselves in both normal and property-based tests.

In normal tests I can use runIf() to throw a ExpectFailureException, but in property-based tests this exception causes the test to fail. If I instead use property-based testing's assume(), it will skip the test during property-based tests, but the thrown AssumptionFailedException causes normal tests to fail.

Is it possible to consolidate ExpectFailureException and AssumptionFailedException to just skip the test in both cases?


An example where I encountered this is when writing tests for implementations of a DisjointSet class.

import io.kotest.core.spec.style.FunSpec
import io.kotest.core.spec.style.funSpec
import io.kotest.core.test.TestScope
import io.kotest.core.test.runIf
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.set
import io.kotest.property.arbitrary.string
import io.kotest.property.checkAll

interface DisjointSet<out E> {
    val elements: Set<E>
    fun toSets(): Collection<Set<E>>
}

class EmptyDisjointSetImpl : DisjointSet<Nothing> {
    override val elements: Set<Nothing> get() = emptySet()
    override fun toSets(): Collection<Set<Nothing>> = emptySet()
}

class UnionFindImpl(sets: Set<Set<String>>) : DisjointSet<String> {
    override val elements: Set<Nothing> get() = emptySet()
    override fun toSets(): Collection<Set<Nothing>> = emptySet()
}

fun testDisjointSetTests(
    newDisjointSet: TestScope.(Set<Set<String>>) -> DisjointSet<String>,
) = funSpec {
    context("toSets") {
        test("should return empty set, when empty") {
            // Arrange
            val disjointSet = newDisjointSet(emptySet())

            // Act/Assert
            disjointSet.toSets() shouldBe emptySet()
        }
    }

    context("property-based tests") {
        test("all elements in toSets are contained in elements") {
            checkAll(Arb.set(Arb.set(Arb.string(1..3), 1..4), 0..5)) { sets ->
                val ds = newDisjointSet(sets)
                val allElements = ds.toSets().flatten().toSet()
                ds.elements shouldBe allElements
            }
        }
    }
}

class UnionFindImplTests : FunSpec({
    include(testDisjointSetTests { sets ->
        UnionFindImpl(sets)
    })
})

class EmptyDisjointSetImplTests : FunSpec({
    include(testDisjointSetTests { ps ->
        runIf { ps.isEmpty() }
        EmptyDisjointSetImpl()
    })
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancement ✨Suggestions for adding new features or improving existing ones.property-testing 📏Related to the property testing mechanisms within the testing framework.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions